New Xenbus driver structure, bringing an explicit state machine to the table.
authoremellor@leeni.uk.xensource.com <emellor@leeni.uk.xensource.com>
Fri, 11 Nov 2005 20:59:05 +0000 (21:59 +0100)
committeremellor@leeni.uk.xensource.com <emellor@leeni.uk.xensource.com>
Fri, 11 Nov 2005 20:59:05 +0000 (21:59 +0100)
This will make the drivers more robust to failure of the other end, and makes
current driver state more visible.  It also allows us to bring very similar
pieces of code from the drivers together into the xenbus driver.  This should
be the start of a fix to the problem with block and net device hotplugging,
because the code was previously confused about the difference between the
connection state of the kernel device layer, and the state of the xenbus
communication.  This confusion is addressed here.

Much of this work is influenced by Rusty Russell's skeleton driver, recently
posted to xen-devel, as that piece made suggestions as to structure and
arrangement that have been taken on board, albeit pushed down into the xenbus
layer.

A new file xenbus_client.c has been added.  This file implements the interface
between the xenbus layer and the layer specific to the driver.  Access to
lower layers is still allowed for those who do not need all the facilities of
the new driver.

xenbus_dev_error has moved from xenbus_xs.c into xenbus_client.c, and
xenbus_dev_ok has been removed, in favour of xenbus_switch_state.

DevController.py now closes down a device by writing a change to the Closing
state on the backend, to allow the frontend time to close down properly.  Its
also writes the frontend and backend details within the same transaction.

The timeout on the hotplug scripts has been cranked down from 2 minutes to 5
seconds.

The diagnose script has been updated to parse the new states.

The blktab and tpm drivers have yet to be ported to this structure.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
13 files changed:
linux-2.6-xen-sparse/drivers/xen/blkback/xenbus.c
linux-2.6-xen-sparse/drivers/xen/blkfront/blkfront.c
linux-2.6-xen-sparse/drivers/xen/blkfront/block.h
linux-2.6-xen-sparse/drivers/xen/netback/xenbus.c
linux-2.6-xen-sparse/drivers/xen/netfront/netfront.c
linux-2.6-xen-sparse/drivers/xen/xenbus/Makefile
linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_client.c [new file with mode: 0644]
linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_probe.c
linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_xs.c
linux-2.6-xen-sparse/include/asm-xen/xenbus.h
tools/python/xen/util/diagnose.py
tools/python/xen/xend/server/DevController.py
tools/python/xen/xend/xenstore/xstransact.py

index 1d5a01a10f01b64edbc35f4d7fd73a34b07cd77c..36ac488b394efa11edac3944436634e6eb508d88 100644 (file)
@@ -1,5 +1,6 @@
 /*  Xenbus code for blkif backend
     Copyright (C) 2005 Rusty Russell <rusty@rustcorp.com.au>
+    Copyright (C) 2005 XenSource Ltd
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
+
+
 #include <stdarg.h>
 #include <linux/module.h>
 #include <asm-xen/xenbus.h>
 #include "common.h"
 
+
+#if 0
+#undef DPRINTK
+#define DPRINTK(fmt, args...) \
+    printk("blkback/xenbus (%s:%d) " fmt ".\n", __FUNCTION__, __LINE__, ##args)
+#endif
+
+
 struct backend_info
 {
        struct xenbus_device *dev;
-
-       /* our communications channel */
        blkif_t *blkif;
+       struct xenbus_watch backend_watch;
 
-       long int frontend_id;
        long int pdev;
        long int readonly;
+};
 
-       /* watch back end for changes */
-       struct xenbus_watch backend_watch;
 
-       /* watch front end for changes */
-       struct xenbus_watch watch;
-       char *frontpath;
-};
+static void maybe_connect(struct backend_info *);
+static void connect(struct backend_info *);
+static int connect_ring(struct backend_info *);
+static void backend_changed(struct xenbus_watch *, const char **,
+                           unsigned int);
+
 
 static int blkback_remove(struct xenbus_device *dev)
 {
        struct backend_info *be = dev->data;
 
-       if (be->watch.node)
-               unregister_xenbus_watch(&be->watch);
-       unregister_xenbus_watch(&be->backend_watch);
-       if (be->blkif)
+       DPRINTK("");
+
+       if (be->backend_watch.node) {
+               unregister_xenbus_watch(&be->backend_watch);
+               kfree(be->backend_watch.node);
+               be->backend_watch.node = NULL;
+       }
+       if (be->blkif) {
                blkif_put(be->blkif);
-       kfree(be->frontpath);
+               be->blkif = NULL;
+       }
        kfree(be);
+       dev->data = NULL;
        return 0;
 }
 
-/* Front end tells us frame. */
-static void frontend_changed(struct xenbus_watch *watch,
-                            const char **vec, unsigned int len)
+
+/**
+ * Entry point to this code when a new device is created.  Allocate the basic
+ * structures, and watch the store waiting for the hotplug scripts to tell us
+ * the device's physical-device.  Switch to InitWait.
+ */
+static int blkback_probe(struct xenbus_device *dev,
+                        const struct xenbus_device_id *id)
 {
-       unsigned long ring_ref;
-       unsigned int evtchn;
        int err;
-       struct xenbus_transaction *xbt;
-       struct backend_info *be
-               = container_of(watch, struct backend_info, watch);
-
-       /* If other end is gone, delete ourself. */
-       if (vec && !xenbus_exists(NULL, be->frontpath, "")) {
-               device_unregister(&be->dev->dev);
-               return;
-       }
-       if (be->blkif == NULL || be->blkif->status == CONNECTED)
-               return;
-
-       err = xenbus_gather(NULL, be->frontpath, "ring-ref", "%lu", &ring_ref,
-                           "event-channel", "%u", &evtchn, NULL);
-       if (err) {
-               xenbus_dev_error(be->dev, err,
-                                "reading %s/ring-ref and event-channel",
-                                be->frontpath);
-               return;
-       }
-
-       /* Map the shared frame, irq etc. */
-       err = blkif_map(be->blkif, ring_ref, evtchn);
-       if (err) {
-               xenbus_dev_error(be->dev, err,
-                                "mapping ring-ref %lu port %u",
-                                ring_ref, evtchn);
-               return;
-       }
-       /* XXX From here on should 'blkif_unmap' on error. */
-
-again:
-       /* Supply the information about the device the frontend needs */
-       xbt = xenbus_transaction_start();
-       if (IS_ERR(xbt)) {
-               xenbus_dev_error(be->dev, err, "starting transaction");
-               return;
+       struct backend_info *be = kmalloc(sizeof(struct backend_info),
+                                         GFP_KERNEL);
+       if (!be) {
+               xenbus_dev_fatal(dev, -ENOMEM,
+                                "allocating backend structure");
+               return -ENOMEM;
        }
+       memset(be, 0, sizeof(*be));
 
-       err = xenbus_printf(xbt, be->dev->nodename, "sectors", "%lu",
-                           vbd_size(&be->blkif->vbd));
-       if (err) {
-               xenbus_dev_error(be->dev, err, "writing %s/sectors",
-                                be->dev->nodename);
-               goto abort;
-       }
+       be->dev = dev;
+       dev->data = be;
 
-       /* FIXME: use a typename instead */
-       err = xenbus_printf(xbt, be->dev->nodename, "info", "%u",
-                           vbd_info(&be->blkif->vbd));
-       if (err) {
-               xenbus_dev_error(be->dev, err, "writing %s/info",
-                                be->dev->nodename);
-               goto abort;
-       }
-       err = xenbus_printf(xbt, be->dev->nodename, "sector-size", "%lu",
-                           vbd_secsize(&be->blkif->vbd));
-       if (err) {
-               xenbus_dev_error(be->dev, err, "writing %s/sector-size",
-                                be->dev->nodename);
-               goto abort;
+       be->blkif = alloc_blkif(dev->otherend_id);
+       if (IS_ERR(be->blkif)) {
+               err = PTR_ERR(be->blkif);
+               be->blkif = NULL;
+               xenbus_dev_fatal(dev, err, "creating block interface");
+               goto fail;
        }
 
-       err = xenbus_transaction_end(xbt, 0);
-       if (err == -EAGAIN)
-               goto again;
-       if (err) {
-               xenbus_dev_error(be->dev, err, "ending transaction",
-                                ring_ref, evtchn);
-               goto abort;
-       }
+       err = xenbus_watch_path2(dev, dev->nodename, "physical-device",
+                                &be->backend_watch, backend_changed);
+       if (err)
+               goto fail;
 
-       xenbus_dev_ok(be->dev);
+       err = xenbus_switch_state(dev, NULL, XenbusStateInitWait);
+       if (err)
+               goto fail;
 
-       return;
+       return 0;
 
- abort:
-       xenbus_transaction_end(xbt, 1);
+fail:
+       DPRINTK("failed");
+       blkback_remove(dev);
+       return err;
 }
 
-/* 
-   Setup supplies physical device.  
-   We provide event channel and device details to front end.
-   Frontend supplies shared frame and event channel.
+
+/**
+ * Callback received when the hotplug scripts have placed the physical-device
+ * node.  Read it and the read-only node, and create a vbd.  If the frontend
+ * is ready, connect.
  */
 static void backend_changed(struct xenbus_watch *watch,
                            const char **vec, unsigned int len)
 {
        int err;
        char *p;
-       long int handle, pdev;
+       long pdev;
        struct backend_info *be
                = container_of(watch, struct backend_info, backend_watch);
        struct xenbus_device *dev = be->dev;
 
+       DPRINTK("");
+
        err = xenbus_scanf(NULL, dev->nodename,
                           "physical-device", "%li", &pdev);
-       if (XENBUS_EXIST_ERR(err))
+       if (XENBUS_EXIST_ERR(err)) {
+               /* Since this watch will fire once immediately after it is
+                  registered, we expect this.  Ignore it, and wait for the
+                  hotplug scripts. */
                return;
-       if (err < 0) {
-               xenbus_dev_error(dev, err, "reading physical-device");
+       }
+       if (err != 1) {
+               xenbus_dev_fatal(dev, err, "reading physical-device");
                return;
        }
        if (be->pdev && be->pdev != pdev) {
                printk(KERN_WARNING
-                      "changing physical-device not supported\n");
+                      "blkback: changing physical-device (from %ld to %ld) "
+                      "not supported.\n", be->pdev, pdev);
                return;
        }
-       be->pdev = pdev;
 
        /* If there's a read-only node, we're read only. */
        p = xenbus_read(NULL, dev->nodename, "read-only", NULL);
@@ -177,125 +160,198 @@ static void backend_changed(struct xenbus_watch *watch,
                kfree(p);
        }
 
-       if (be->blkif == NULL) {
+       if (be->pdev == 0L) {
                /* Front end dir is a number, which is used as the handle. */
-               p = strrchr(be->frontpath, '/') + 1;
+
+               long handle;
+
+               p = strrchr(dev->otherend, '/') + 1;
                handle = simple_strtoul(p, NULL, 0);
 
-               be->blkif = alloc_blkif(be->frontend_id);
-               if (IS_ERR(be->blkif)) {
-                       err = PTR_ERR(be->blkif);
-                       be->blkif = NULL;
-                       xenbus_dev_error(dev, err,
-                                        "creating block interface");
-                       return;
-               }
+               be->pdev = pdev;
 
                err = vbd_create(be->blkif, handle, be->pdev, be->readonly);
                if (err) {
-                       blkif_put(be->blkif);
-                       be->blkif = NULL;
-                       xenbus_dev_error(dev, err,
+                       be->pdev = 0L;
+                       xenbus_dev_fatal(dev, err,
                                         "creating vbd structure");
                        return;
                }
 
-               /* Pass in NULL node to skip exist test. */
-               frontend_changed(&be->watch, NULL, 0);
+               maybe_connect(be);
        }
 }
 
-static int blkback_probe(struct xenbus_device *dev,
-                        const struct xenbus_device_id *id)
+
+/**
+ * Callback received when the frontend's state changes.
+ */
+static void frontend_changed(struct xenbus_device *dev,
+                            XenbusState frontend_state)
 {
-       struct backend_info *be;
-       char *frontend;
+       struct backend_info *be = dev->data;
        int err;
 
-       be = kmalloc(sizeof(*be), GFP_KERNEL);
-       if (!be) {
-               xenbus_dev_error(dev, -ENOMEM,
-                                "allocating backend structure");
-               return -ENOMEM;
+       DPRINTK("");
+
+       switch (frontend_state) {
+       case XenbusStateInitialising:
+       case XenbusStateConnected:
+               break;
+
+       case XenbusStateInitialised:
+               err = connect_ring(be);
+               if (err) {
+                       return;
+               }
+               maybe_connect(be);
+               break;
+
+       case XenbusStateClosing:
+               xenbus_switch_state(dev, NULL, XenbusStateClosing);
+               break;
+
+       case XenbusStateClosed:
+               device_unregister(&dev->dev);
+               break;
+
+       case XenbusStateUnknown:
+       case XenbusStateInitWait:
+       default:
+               xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",
+                                frontend_state);
+               break;
        }
-       memset(be, 0, sizeof(*be));
+}
 
-       frontend = NULL;
-       err = xenbus_gather(NULL, dev->nodename,
-                           "frontend-id", "%li", &be->frontend_id,
-                           "frontend", NULL, &frontend,
-                           NULL);
-       if (XENBUS_EXIST_ERR(err))
-               goto free_be;
-       if (err < 0) {
-               xenbus_dev_error(dev, err,
-                                "reading %s/frontend or frontend-id",
-                                dev->nodename);
-               goto free_be;
+
+/* ** Connection ** */
+
+
+static void maybe_connect(struct backend_info *be)
+{
+       if (be->pdev != 0L && be->blkif->status == CONNECTED) {
+               connect(be);
        }
-       if (strlen(frontend) == 0 || !xenbus_exists(NULL, frontend, "")) {
-               /* If we can't get a frontend path and a frontend-id,
-                * then our bus-id is no longer valid and we need to
-                * destroy the backend device.
-                */
-               err = -ENOENT;
-               goto free_be;
+}
+
+
+/**
+ * Write the physical details regarding the block device to the store, and
+ * switch to Connected state.
+ */
+static void connect(struct backend_info *be)
+{
+       struct xenbus_transaction *xbt;
+       int err;
+       struct xenbus_device *dev = be->dev;
+
+       DPRINTK("%s", dev->otherend);
+
+       /* Supply the information about the device the frontend needs */
+again:
+       xbt = xenbus_transaction_start();
+
+       if (IS_ERR(xbt)) {
+               err = PTR_ERR(xbt);
+               xenbus_dev_fatal(dev, err, "starting transaction");
+               return;
        }
 
-       be->dev = dev;
-       be->backend_watch.node = dev->nodename;
-       be->backend_watch.callback = backend_changed;
-       /* Will implicitly call backend_changed once. */
-       err = register_xenbus_watch(&be->backend_watch);
+       err = xenbus_printf(xbt, dev->nodename, "sectors", "%lu",
+                           vbd_size(&be->blkif->vbd));
        if (err) {
-               be->backend_watch.node = NULL;
-               xenbus_dev_error(dev, err,
-                                "adding backend watch on %s",
+               xenbus_dev_fatal(dev, err, "writing %s/sectors",
+                                dev->nodename);
+               goto abort;
+       }
+
+       /* FIXME: use a typename instead */
+       err = xenbus_printf(xbt, dev->nodename, "info", "%u",
+                           vbd_info(&be->blkif->vbd));
+       if (err) {
+               xenbus_dev_fatal(dev, err, "writing %s/info",
                                 dev->nodename);
-               goto free_be;
+               goto abort;
+       }
+       err = xenbus_printf(xbt, dev->nodename, "sector-size", "%lu",
+                           vbd_secsize(&be->blkif->vbd));
+       if (err) {
+               xenbus_dev_fatal(dev, err, "writing %s/sector-size",
+                                dev->nodename);
+               goto abort;
        }
 
-       be->frontpath = frontend;
-       be->watch.node = be->frontpath;
-       be->watch.callback = frontend_changed;
-       err = register_xenbus_watch(&be->watch);
+       err = xenbus_switch_state(dev, xbt, XenbusStateConnected);
+       if (err)
+               goto abort;
+
+       err = xenbus_transaction_end(xbt, 0);
+       if (err == -EAGAIN)
+               goto again;
+       if (err)
+               xenbus_dev_fatal(dev, err, "ending transaction");
+       return;
+ abort:
+       xenbus_transaction_end(xbt, 1);
+}
+
+
+static int connect_ring(struct backend_info *be)
+{
+       struct xenbus_device *dev = be->dev;
+       unsigned long ring_ref;
+       unsigned int evtchn;
+       int err;
+
+       DPRINTK("%s", dev->otherend);
+
+       err = xenbus_gather(NULL, dev->otherend, "ring-ref", "%lu", &ring_ref,
+                           "event-channel", "%u", &evtchn, NULL);
        if (err) {
-               be->watch.node = NULL;
-               xenbus_dev_error(dev, err,
-                                "adding frontend watch on %s",
-                                be->frontpath);
-               goto free_be;
+               xenbus_dev_fatal(dev, err,
+                                "reading %s/ring-ref and event-channel",
+                                dev->otherend);
+               return err;
        }
 
-       dev->data = be;
-       return 0;
+       /* Map the shared frame, irq etc. */
+       err = blkif_map(be->blkif, ring_ref, evtchn);
+       if (err) {
+               xenbus_dev_fatal(dev, err, "mapping ring-ref %lu port %u",
+                                ring_ref, evtchn);
+               return err;
+       }
 
- free_be:
-       if (be->backend_watch.node)
-               unregister_xenbus_watch(&be->backend_watch);
-       kfree(frontend);
-       kfree(be);
-       return err;
+       return 0;
 }
 
+
+/* ** Driver Registration ** */
+
+
 static struct xenbus_device_id blkback_ids[] = {
        { "vbd" },
        { "" }
 };
 
+
 static struct xenbus_driver blkback = {
        .name = "vbd",
        .owner = THIS_MODULE,
        .ids = blkback_ids,
        .probe = blkback_probe,
        .remove = blkback_remove,
+       .otherend_changed = frontend_changed
 };
 
+
 void blkif_xenbus_init(void)
 {
        xenbus_register_backend(&blkback);
 }
 
+
 /*
  * Local variables:
  *  c-file-style: "linux"
index a865e88f319e8991e35623385abb50b1631c6b27..5f22d6af66ad1a1ded4d719a3f44c9a5c8a2cd03 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright (c) 2004, Christian Limpach
  * Copyright (c) 2004, Andrew Warfield
  * Copyright (c) 2005, Christopher Clark
+ * Copyright (c) 2005, XenSource Ltd
  * 
  * This file may be distributed separately from the Linux kernel, or
  * incorporated into other software packages, subject to the following license:
@@ -31,6 +32,7 @@
  * IN THE SOFTWARE.
  */
 
+
 #if 1
 #define ASSERT(p)                                                         \
        if (!(p)) { printk("Assertion '%s' failed, line %d, file %s", #p , \
@@ -39,6 +41,7 @@
 #define ASSERT(_p)
 #endif
 
+
 #include <linux/version.h>
 #include "block.h"
 #include <linux/cdrom.h>
@@ -51,6 +54,7 @@
 #include <asm-xen/gnttab.h>
 #include <asm/hypervisor.h>
 
+
 #define BLKIF_STATE_DISCONNECTED 0
 #define BLKIF_STATE_CONNECTED    1
 
     (BLKIF_MAX_SEGMENTS_PER_REQUEST * BLKIF_RING_SIZE)
 #define GRANT_INVALID_REF      0
 
-static void kick_pending_request_queues(struct blkfront_info *info);
 
-static void blkif_completion(struct blk_shadow *s);
+static void connect(struct blkfront_info *);
+static void blkfront_closing(struct xenbus_device *);
+static int blkfront_remove(struct xenbus_device *);
+static int talk_to_backend(struct xenbus_device *, struct blkfront_info *);
+static int setup_blkring(struct xenbus_device *, struct blkfront_info *);
+
+static void kick_pending_request_queues(struct blkfront_info *);
+
+static irqreturn_t blkif_int(int irq, void *dev_id, struct pt_regs *ptregs);
+static void blkif_restart_queue(void *arg);
+static void blkif_recover(struct blkfront_info *);
+static void blkif_completion(struct blk_shadow *);
+static void blkif_free(struct blkfront_info *);
+
+
+/**
+ * Entry point to this code when a new device is created.  Allocate the basic
+ * structures and the ring buffer for communication with the backend, and
+ * inform the backend of the appropriate details for those.  Switch to
+ * Initialised state.
+ */
+static int blkfront_probe(struct xenbus_device *dev,
+                         const struct xenbus_device_id *id)
+{
+       int err, vdevice, i;
+       struct blkfront_info *info;
+
+       /* FIXME: Use dynamic device id if this is not set. */
+       err = xenbus_scanf(NULL, dev->nodename,
+                          "virtual-device", "%i", &vdevice);
+       if (err != 1) {
+               xenbus_dev_fatal(dev, err, "reading virtual-device");
+               return err;
+       }
+
+       info = kmalloc(sizeof(*info), GFP_KERNEL);
+       if (!info) {
+               xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
+               return -ENOMEM;
+       }
+       info->xbdev = dev;
+       info->vdevice = vdevice;
+       info->connected = BLKIF_STATE_DISCONNECTED;
+       info->mi = NULL;
+       info->gd = NULL;
+       INIT_WORK(&info->work, blkif_restart_queue, (void *)info);
+
+       info->shadow_free = 0;
+       memset(info->shadow, 0, sizeof(info->shadow));
+       for (i = 0; i < BLK_RING_SIZE; i++)
+               info->shadow[i].req.id = i+1;
+       info->shadow[BLK_RING_SIZE-1].req.id = 0x0fffffff;
+
+       /* Front end dir is a number, which is used as the id. */
+       info->handle = simple_strtoul(strrchr(dev->nodename,'/')+1, NULL, 0);
+       dev->data = info;
+
+       err = talk_to_backend(dev, info);
+       if (err) {
+               kfree(info);
+               dev->data = NULL;
+               return err;
+       }
+
+       return 0;
+}
+
+
+/**
+ * We are reconnecting to the backend, due to a suspend/resume, or a backend
+ * driver restart.  We tear down our blkif structure and recreate it, but
+ * leave the device-layer structures intact so that this is transparent to the
+ * rest of the kernel.
+ */
+static int blkfront_resume(struct xenbus_device *dev)
+{
+       struct blkfront_info *info = dev->data;
+       int err;
+
+       DPRINTK("blkfront_resume: %s\n", dev->nodename);
+
+       blkif_free(info);
+
+       err = talk_to_backend(dev, info);
+       if (!err)
+               blkif_recover(info);
+
+       return err;
+}
+
+
+/* Common code used when first setting up, and when resuming. */
+static int talk_to_backend(struct xenbus_device *dev,
+                          struct blkfront_info *info)
+{
+       const char *message = NULL;
+       struct xenbus_transaction *xbt;
+       int err;
+
+       /* Create shared ring, alloc event channel. */
+       err = setup_blkring(dev, info);
+       if (err)
+               goto out;
+
+again:
+       xbt = xenbus_transaction_start();
+       if (IS_ERR(xbt)) {
+               xenbus_dev_fatal(dev, err, "starting transaction");
+               goto destroy_blkring;
+       }
+
+       err = xenbus_printf(xbt, dev->nodename,
+                           "ring-ref","%u", info->ring_ref);
+       if (err) {
+               message = "writing ring-ref";
+               goto abort_transaction;
+       }
+       err = xenbus_printf(xbt, dev->nodename,
+                           "event-channel", "%u", info->evtchn);
+       if (err) {
+               message = "writing event-channel";
+               goto abort_transaction;
+       }
+
+       err = xenbus_switch_state(dev, xbt, XenbusStateInitialised);
+       if (err) {
+               goto abort_transaction;
+       }
+
+       err = xenbus_transaction_end(xbt, 0);
+       if (err) {
+               if (err == -EAGAIN)
+                       goto again;
+               xenbus_dev_fatal(dev, err, "completing transaction");
+               goto destroy_blkring;
+       }
+
+       return 0;
+
+ abort_transaction:
+       xenbus_transaction_end(xbt, 1);
+       if (message)
+               xenbus_dev_fatal(dev, err, "%s", message);
+ destroy_blkring:
+       blkif_free(info);
+ out:
+       return err;
+}
+
+
+static int setup_blkring(struct xenbus_device *dev,
+                        struct blkfront_info *info)
+{
+       blkif_sring_t *sring;
+       int err;
+
+       info->ring_ref = GRANT_INVALID_REF;
+
+       sring = (blkif_sring_t *)__get_free_page(GFP_KERNEL);
+       if (!sring) {
+               xenbus_dev_fatal(dev, -ENOMEM, "allocating shared ring");
+               return -ENOMEM;
+       }
+       SHARED_RING_INIT(sring);
+       FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE);
+
+       err = xenbus_grant_ring(dev, virt_to_mfn(info->ring.sring));
+       if (err < 0) {
+               free_page((unsigned long)sring);
+               info->ring.sring = NULL;
+               goto fail;
+       }
+       info->ring_ref = err;
+
+       err = xenbus_alloc_evtchn(dev, &info->evtchn);
+       if (err)
+               goto fail;
+
+       err = bind_evtchn_to_irqhandler(
+               info->evtchn, blkif_int, SA_SAMPLE_RANDOM, "blkif", info);
+       if (err <= 0) {
+               xenbus_dev_fatal(dev, err,
+                                "bind_evtchn_to_irqhandler failed");
+               goto fail;
+       }
+       info->irq = err;
+
+       return 0;
+fail:
+       blkif_free(info);
+       return err;
+}
+
+
+/**
+ * Callback received when the backend's state changes.
+ */
+static void backend_changed(struct xenbus_device *dev,
+                           XenbusState backend_state)
+{
+       struct blkfront_info *info = dev->data;
+
+       DPRINTK("blkfront:backend_changed.\n");
+
+       switch (backend_state) {
+       case XenbusStateUnknown:
+       case XenbusStateInitialising:
+       case XenbusStateInitWait:
+       case XenbusStateInitialised:
+       case XenbusStateClosed:
+               break;
+
+       case XenbusStateConnected:
+               connect(info);
+               break;
+
+       case XenbusStateClosing:
+               blkfront_closing(dev);
+               break;
+       }
+}
+
+
+/* ** Connection ** */
+
+
+static void connect(struct blkfront_info *info)
+{
+       unsigned long sectors, sector_size;
+       unsigned int binfo;
+
+        if (info->connected == BLKIF_STATE_CONNECTED)
+               return;
+
+       DPRINTK("blkfront.c:connect:%s.\n", info->xbdev->otherend);
+
+       int err = xenbus_gather(NULL, info->xbdev->otherend,
+                               "sectors", "%lu", &sectors,
+                               "info", "%u", &binfo,
+                               "sector-size", "%lu", &sector_size,
+                               NULL);
+       if (err) {
+               xenbus_dev_fatal(info->xbdev, err,
+                                "reading backend fields at %s",
+                                info->xbdev->otherend);
+               return;
+       }
+       
+        info->connected = BLKIF_STATE_CONNECTED;
+        xlvbd_add(sectors, info->vdevice, binfo, sector_size, info);
+       
+       err = xenbus_switch_state(info->xbdev, NULL, XenbusStateConnected);
+       if (err)
+               return;
+       
+       /* Kick pending requests. */
+       spin_lock_irq(&blkif_io_lock);
+       kick_pending_request_queues(info);
+       spin_unlock_irq(&blkif_io_lock);
+}
+
+
+/**
+ * Handle the change of state of the backend to Closing.  We must delete our
+ * device-layer structures now, to ensure that writes are flushed through to
+ * the backend.  Once is this done, we can switch to Closed in
+ * acknowledgement.
+ */
+static void blkfront_closing(struct xenbus_device *dev)
+{
+       struct blkfront_info *info = dev->data;
+
+       DPRINTK("blkfront_closing: %s removed\n", dev->nodename);
+
+       if (info->mi) {
+               DPRINTK("Calling xlvbd_del\n");
+               xlvbd_del(info);
+               info->mi = NULL;
+       }
+
+       xenbus_switch_state(dev, NULL, XenbusStateClosed);
+}
+
+
+static int blkfront_remove(struct xenbus_device *dev)
+{
+       DPRINTK("blkfront_remove: %s removed\n", dev->nodename);
+
+       struct blkfront_info *info = dev->data;
+
+       blkif_free(info);
+
+       kfree(info);
+
+       return 0;
+}
+
 
 static inline int GET_ID_FROM_FREELIST(
        struct blkfront_info *info)
@@ -214,7 +513,7 @@ static int blkif_queue_request(struct request *req)
 
                        gnttab_grant_foreign_access_ref(
                                ref,
-                               info->backend_id,
+                               info->xbdev->otherend_id,
                                buffer_mfn,
                                rq_data_dir(req) );
 
@@ -360,6 +659,15 @@ static void blkif_free(struct blkfront_info *info)
        if (info->irq)
                unbind_from_irqhandler(info->irq, info); 
        info->evtchn = info->irq = 0;
+
+}
+
+static void blkif_completion(struct blk_shadow *s)
+{
+       int i;
+       for (i = 0; i < s->req.nr_segments; i++)
+               gnttab_end_foreign_access(
+                       blkif_gref_from_fas(s->req.frame_and_sects[i]), 0, 0UL);
 }
 
 static void blkif_recover(struct blkfront_info *info)
@@ -400,7 +708,7 @@ static void blkif_recover(struct blkfront_info *info)
                for (j = 0; j < req->nr_segments; j++)
                        gnttab_grant_foreign_access_ref(
                                blkif_gref_from_fas(req->frame_and_sects[j]),
-                               info->backend_id,
+                               info->xbdev->otherend_id,
                                pfn_to_mfn(info->shadow[req->id].frame[j]),
                                rq_data_dir(
                                        (struct request *)
@@ -422,21 +730,8 @@ static void blkif_recover(struct blkfront_info *info)
        info->connected = BLKIF_STATE_CONNECTED;
 }
 
-static void blkif_connect(struct blkfront_info *info, u16 evtchn)
-{
-       int err = 0;
-
-       info->evtchn = evtchn;
 
-       err = bind_evtchn_to_irqhandler(
-               info->evtchn, blkif_int, SA_SAMPLE_RANDOM, "blkif", info);
-       if (err <= 0) {
-               WPRINTK("bind_evtchn_to_irqhandler failed (err=%d)\n", err);
-               return;
-       }
-
-       info->irq = err;
-}
+/* ** Driver Registration ** */
 
 
 static struct xenbus_device_id blkfront_ids[] = {
@@ -444,278 +739,6 @@ static struct xenbus_device_id blkfront_ids[] = {
        { "" }
 };
 
-static void watch_for_status(struct xenbus_watch *watch,
-                            const char **vec, unsigned int len)
-{
-       struct blkfront_info *info;
-       unsigned int binfo;
-       unsigned long sectors, sector_size;
-       int err;
-       const char *node;
-
-       node = vec[XS_WATCH_PATH];
-
-       info = container_of(watch, struct blkfront_info, watch);
-       node += strlen(watch->node);
-
-       /* FIXME: clean up when error on the other end. */
-       if ((info->connected == BLKIF_STATE_CONNECTED) || info->mi)
-               return;
-
-       err = xenbus_gather(NULL, watch->node,
-                           "sectors", "%lu", &sectors,
-                           "info", "%u", &binfo,
-                           "sector-size", "%lu", &sector_size,
-                           NULL);
-       if (err) {
-               xenbus_dev_error(info->xbdev, err,
-                                "reading backend fields at %s", watch->node);
-               return;
-       }
-
-       info->connected = BLKIF_STATE_CONNECTED;
-       xlvbd_add(sectors, info->vdevice, binfo, sector_size, info);
-
-       xenbus_dev_ok(info->xbdev);
-
-       /* Kick pending requests. */
-       spin_lock_irq(&blkif_io_lock);
-       kick_pending_request_queues(info);
-       spin_unlock_irq(&blkif_io_lock);
-}
-
-static int setup_blkring(struct xenbus_device *dev, struct blkfront_info *info)
-{
-       blkif_sring_t *sring;
-       int err;
-       evtchn_op_t op = {
-               .cmd = EVTCHNOP_alloc_unbound,
-               .u.alloc_unbound.dom = DOMID_SELF,
-               .u.alloc_unbound.remote_dom = info->backend_id };
-
-       info->ring_ref = GRANT_INVALID_REF;
-
-       sring = (void *)__get_free_page(GFP_KERNEL);
-       if (!sring) {
-               xenbus_dev_error(dev, -ENOMEM, "allocating shared ring");
-               return -ENOMEM;
-       }
-       SHARED_RING_INIT(sring);
-       FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE);
-
-       err = gnttab_grant_foreign_access(info->backend_id,
-                                         virt_to_mfn(info->ring.sring), 0);
-       if (err == -ENOSPC) {
-               free_page((unsigned long)info->ring.sring);
-               info->ring.sring = 0;
-               xenbus_dev_error(dev, err, "granting access to ring page");
-               return err;
-       }
-       info->ring_ref = err;
-
-       err = HYPERVISOR_event_channel_op(&op);
-       if (err) {
-               gnttab_end_foreign_access(info->ring_ref, 0,
-                                         (unsigned long)info->ring.sring);
-               info->ring_ref = GRANT_INVALID_REF;
-               info->ring.sring = NULL;
-               xenbus_dev_error(dev, err, "allocating event channel");
-               return err;
-       }
-
-       blkif_connect(info, op.u.alloc_unbound.port);
-
-       return 0;
-}
-
-/* Common code used when first setting up, and when resuming. */
-static int talk_to_backend(struct xenbus_device *dev,
-                          struct blkfront_info *info)
-{
-       char *backend;
-       const char *message;
-       struct xenbus_transaction *xbt;
-       int err;
-
-       backend = NULL;
-       err = xenbus_gather(NULL, dev->nodename,
-                           "backend-id", "%i", &info->backend_id,
-                           "backend", NULL, &backend,
-                           NULL);
-       if (XENBUS_EXIST_ERR(err))
-               goto out;
-       if (backend && strlen(backend) == 0) {
-               err = -ENOENT;
-               goto out;
-       }
-       if (err < 0) {
-               xenbus_dev_error(dev, err, "reading %s/backend or backend-id",
-                                dev->nodename);
-               goto out;
-       }
-
-       /* Create shared ring, alloc event channel. */
-       err = setup_blkring(dev, info);
-       if (err) {
-               xenbus_dev_error(dev, err, "setting up block ring");
-               goto out;
-       }
-
-again:
-       xbt = xenbus_transaction_start();
-       if (IS_ERR(xbt)) {
-               xenbus_dev_error(dev, err, "starting transaction");
-               goto destroy_blkring;
-       }
-
-       err = xenbus_printf(xbt, dev->nodename,
-                           "ring-ref","%u", info->ring_ref);
-       if (err) {
-               message = "writing ring-ref";
-               goto abort_transaction;
-       }
-       err = xenbus_printf(xbt, dev->nodename,
-                           "event-channel", "%u", info->evtchn);
-       if (err) {
-               message = "writing event-channel";
-               goto abort_transaction;
-       }
-
-       err = xenbus_transaction_end(xbt, 0);
-       if (err) {
-               if (err == -EAGAIN)
-                       goto again;
-               xenbus_dev_error(dev, err, "completing transaction");
-               goto destroy_blkring;
-       }
-
-       info->watch.node = backend;
-       info->watch.callback = watch_for_status;
-       err = register_xenbus_watch(&info->watch);
-       if (err) {
-               message = "registering watch on backend";
-               goto destroy_blkring;
-       }
-
-       info->backend = backend;
-
-       return 0;
-
- abort_transaction:
-       xenbus_transaction_end(xbt, 1);
-       xenbus_dev_error(dev, err, "%s", message);
- destroy_blkring:
-       blkif_free(info);
- out:
-       kfree(backend);
-       return err;
-}
-
-/* Setup supplies the backend dir, virtual device.
-
-   We place an event channel and shared frame entries.
-   We watch backend to wait if it's ok. */
-static int blkfront_probe(struct xenbus_device *dev,
-                         const struct xenbus_device_id *id)
-{
-       int err, vdevice, i;
-       struct blkfront_info *info;
-
-       /* FIXME: Use dynamic device id if this is not set. */
-       err = xenbus_scanf(NULL, dev->nodename,
-                          "virtual-device", "%i", &vdevice);
-       if (XENBUS_EXIST_ERR(err))
-               return err;
-       if (err < 0) {
-               xenbus_dev_error(dev, err, "reading virtual-device");
-               return err;
-       }
-
-       info = kmalloc(sizeof(*info), GFP_KERNEL);
-       if (!info) {
-               xenbus_dev_error(dev, err, "allocating info structure");
-               return err;
-       }
-       info->xbdev = dev;
-       info->vdevice = vdevice;
-       info->connected = BLKIF_STATE_DISCONNECTED;
-       info->mi = NULL;
-       info->gd = NULL;
-       INIT_WORK(&info->work, blkif_restart_queue, (void *)info);
-
-       info->shadow_free = 0;
-       memset(info->shadow, 0, sizeof(info->shadow));
-       for (i = 0; i < BLK_RING_SIZE; i++)
-               info->shadow[i].req.id = i+1;
-       info->shadow[BLK_RING_SIZE-1].req.id = 0x0fffffff;
-
-       /* Front end dir is a number, which is used as the id. */
-       info->handle = simple_strtoul(strrchr(dev->nodename,'/')+1, NULL, 0);
-       dev->data = info;
-
-       err = talk_to_backend(dev, info);
-       if (err) {
-               kfree(info);
-               dev->data = NULL;
-               return err;
-       }
-
-       {
-               unsigned int len = max(XS_WATCH_PATH, XS_WATCH_TOKEN) + 1;
-               const char *vec[len];
-
-               vec[XS_WATCH_PATH] = info->watch.node;
-               vec[XS_WATCH_TOKEN] = NULL;
-
-               /* Call once in case entries already there. */
-               watch_for_status(&info->watch, vec, len);
-       }
-
-       return 0;
-}
-
-static int blkfront_remove(struct xenbus_device *dev)
-{
-       struct blkfront_info *info = dev->data;
-
-       if (info->backend)
-               unregister_xenbus_watch(&info->watch);
-
-       if (info->mi)
-               xlvbd_del(info);
-
-       blkif_free(info);
-
-       kfree(info->backend);
-       kfree(info);
-
-       return 0;
-}
-
-static int blkfront_suspend(struct xenbus_device *dev)
-{
-       struct blkfront_info *info = dev->data;
-
-       unregister_xenbus_watch(&info->watch);
-       kfree(info->backend);
-       info->backend = NULL;
-
-       return 0;
-}
-
-static int blkfront_resume(struct xenbus_device *dev)
-{
-       struct blkfront_info *info = dev->data;
-       int err;
-
-       blkif_free(info);
-
-       err = talk_to_backend(dev, info);
-       if (!err)
-               blkif_recover(info);
-
-       return err;
-}
 
 static struct xenbus_driver blkfront = {
        .name = "vbd",
@@ -724,27 +747,28 @@ static struct xenbus_driver blkfront = {
        .probe = blkfront_probe,
        .remove = blkfront_remove,
        .resume = blkfront_resume,
-       .suspend = blkfront_suspend,
+       .otherend_changed = backend_changed,
 };
 
+
 static int __init xlblk_init(void)
 {
        if (xen_init() < 0)
                return -ENODEV;
 
-       xenbus_register_driver(&blkfront);
-       return 0;
+       return xenbus_register_frontend(&blkfront);
 }
-
 module_init(xlblk_init);
 
-static void blkif_completion(struct blk_shadow *s)
+
+static void xlblk_exit(void)
 {
-       int i;
-       for (i = 0; i < s->req.nr_segments; i++)
-               gnttab_end_foreign_access(
-                       blkif_gref_from_fas(s->req.frame_and_sects[i]), 0, 0UL);
+       return xenbus_unregister_driver(&blkfront);
 }
+module_exit(xlblk_exit);
+
+MODULE_LICENSE("BSD");
+
 
 /*
  * Local variables:
index c3149f5deea0b3f46058a62615c83ca6e4ebfcd3..35f14403880a7930faeef20cf9f60c5f31b5d2a1 100644 (file)
@@ -113,15 +113,11 @@ struct blk_shadow {
 struct blkfront_info
 {
        struct xenbus_device *xbdev;
-       /* We watch the backend */
-       struct xenbus_watch watch;
        dev_t dev;
        struct gendisk *gd;
        int vdevice;
        blkif_vdev_t handle;
        int connected;
-       char *backend;
-       int backend_id;
        int ring_ref;
        blkif_front_ring_t ring;
        unsigned int evtchn, irq;
index 44b7391425bb572602515ca0e03193fadc72f20d..7f4958038b18971a21b013d479a04b7a341a1de1 100644 (file)
@@ -1,5 +1,6 @@
 /*  Xenbus code for netif backend
     Copyright (C) 2005 Rusty Russell <rusty@rustcorp.com.au>
+    Copyright (C) 2005 XenSource Ltd
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
+
+
 #include <stdarg.h>
 #include <linux/module.h>
 #include <asm-xen/xenbus.h>
+#include <asm-xen/net_driver_util.h>
 #include "common.h"
 
+
+#if 0
+#undef DPRINTK
+#define DPRINTK(fmt, args...) \
+    printk("netback/xenbus (%s:%d) " fmt ".\n", __FUNCTION__, __LINE__, ##args)
+#endif
+
+
 struct backend_info
 {
        struct xenbus_device *dev;
-
-       /* our communications channel */
        netif_t *netif;
+       struct xenbus_watch backend_watch;
+       XenbusState frontend_state;
+};
 
-       long int frontend_id;
 
-       /* watch back end for changes */
-       struct xenbus_watch backend_watch;
+static int connect_rings(struct backend_info *);
+static void connect(struct backend_info *);
+static void maybe_connect(struct backend_info *);
+static void backend_changed(struct xenbus_watch *, const char **,
+                           unsigned int);
 
-       /* watch front end for changes */
-       struct xenbus_watch watch;
-       char *frontpath;
-};
 
 static int netback_remove(struct xenbus_device *dev)
 {
        struct backend_info *be = dev->data;
 
-       if (be->watch.node)
-               unregister_xenbus_watch(&be->watch);
-       unregister_xenbus_watch(&be->backend_watch);
-       if (be->netif)
+       if (be->backend_watch.node) {
+               unregister_xenbus_watch(&be->backend_watch);
+               kfree(be->backend_watch.node);
+               be->backend_watch.node = NULL;
+       }
+       if (be->netif) {
                netif_disconnect(be->netif);
-       kfree(be->frontpath);
+               be->netif = NULL;
+       }
        kfree(be);
+       dev->data = NULL;
        return 0;
 }
 
-/* Front end tells us frame. */
-static void frontend_changed(struct xenbus_watch *watch, 
-                            const char **vec, unsigned int len)
+
+/**
+ * Entry point to this code when a new device is created.  Allocate the basic
+ * structures, and watch the store waiting for the hotplug scripts to tell us
+ * the device's handle.  Switch to InitWait.
+ */
+static int netback_probe(struct xenbus_device *dev,
+                        const struct xenbus_device_id *id)
 {
-       unsigned long tx_ring_ref, rx_ring_ref;
-       unsigned int evtchn;
        int err;
-       struct backend_info *be
-               = container_of(watch, struct backend_info, watch);
-       char *mac, *e, *s;
-       int i;
-
-       /* If other end is gone, delete ourself. */
-       if (vec && !xenbus_exists(NULL, be->frontpath, "")) {
-               xenbus_rm(NULL, be->dev->nodename, "");
-               device_unregister(&be->dev->dev);
-               return;
+       struct backend_info *be = kmalloc(sizeof(struct backend_info),
+                                         GFP_KERNEL);
+       if (!be) {
+               xenbus_dev_fatal(dev, -ENOMEM,
+                                "allocating backend structure");
+               return -ENOMEM;
        }
-       if (be->netif == NULL || be->netif->status == CONNECTED)
-               return;
+       memset(be, 0, sizeof(*be));
 
-       mac = xenbus_read(NULL, be->frontpath, "mac", NULL);
-       if (IS_ERR(mac)) {
-               err = PTR_ERR(mac);
-               xenbus_dev_error(be->dev, err, "reading %s/mac",
-                                be->dev->nodename);
-               return;
-       }
-       s = mac;
-       for (i = 0; i < ETH_ALEN; i++) {
-               be->netif->fe_dev_addr[i] = simple_strtoul(s, &e, 16);
-               if (s == e || (e[0] != ':' && e[0] != 0)) {
-                       kfree(mac);
-                       err = -ENOENT;
-                       xenbus_dev_error(be->dev, err, "parsing %s/mac",
-                                        be->dev->nodename);
-                       return;
-               }
-               s = &e[1];
-       }
-       kfree(mac);
+       be->dev = dev;
+       dev->data = be;
 
-       err = xenbus_gather(NULL, be->frontpath,
-                           "tx-ring-ref", "%lu", &tx_ring_ref,
-                           "rx-ring-ref", "%lu", &rx_ring_ref,
-                           "event-channel", "%u", &evtchn, NULL);
+       err = xenbus_watch_path2(dev, dev->nodename, "handle",
+                                &be->backend_watch, backend_changed);
+       if (err)
+               goto fail;
+
+       err = xenbus_switch_state(dev, NULL, XenbusStateInitWait);
        if (err) {
-               xenbus_dev_error(be->dev, err,
-                                "reading %s/ring-ref and event-channel",
-                                be->frontpath);
-               return;
+               goto fail;
        }
 
-       /* Map the shared frame, irq etc. */
-       err = netif_map(be->netif, tx_ring_ref, rx_ring_ref, evtchn);
-       if (err) {
-               xenbus_dev_error(be->dev, err,
-                                "mapping shared-frames %lu/%lu port %u",
-                                tx_ring_ref, rx_ring_ref, evtchn);
-               return;
+       return 0;
+
+fail:
+       DPRINTK("failed");
+       netback_remove(dev);
+       return err;
+}
+
+
+/**
+ * Handle the creation of the hotplug script environment.  We add the script
+ * and vif variables to the environment, for the benefit of the vif-* hotplug
+ * scripts.
+ */
+static int netback_hotplug(struct xenbus_device *xdev, char **envp,
+                          int num_envp, char *buffer, int buffer_size)
+{
+       struct backend_info *be = xdev->data;
+       netif_t *netif = be->netif;
+       int i = 0, length = 0;
+
+       DPRINTK("netback_hotplug");
+
+       char *val = xenbus_read(NULL, xdev->nodename, "script", NULL);
+       if (IS_ERR(val)) {
+               int err = PTR_ERR(val);
+               xenbus_dev_fatal(xdev, err, "reading script");
+               return err;
+       }
+       else {
+               add_hotplug_env_var(envp, num_envp, &i,
+                                   buffer, buffer_size, &length,
+                                   "script=%s", val);
+               kfree(val);
        }
 
-       xenbus_dev_ok(be->dev);
+       add_hotplug_env_var(envp, num_envp, &i,
+                           buffer, buffer_size, &length,
+                           "vif=%s", netif->dev->name);
+
+       envp[i] = NULL;
 
-       return;
+       return 0;
 }
 
-/* 
-   Setup supplies physical device.  
  We provide event channel and device details to front end.
  Frontend supplies shared frame and event channel.
+
+/**
* Callback received when the hotplug scripts have placed the handle node.
* Read it, and create a netif structure.  If the frontend is ready, connect.
  */
 static void backend_changed(struct xenbus_watch *watch,
                            const char **vec, unsigned int len)
 {
        int err;
-       long int handle;
+       long handle;
        struct backend_info *be
                = container_of(watch, struct backend_info, backend_watch);
        struct xenbus_device *dev = be->dev;
-       u8 be_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+
+       DPRINTK("");
 
        err = xenbus_scanf(NULL, dev->nodename, "handle", "%li", &handle);
-       if (XENBUS_EXIST_ERR(err))
+       if (XENBUS_EXIST_ERR(err)) {
+               /* Since this watch will fire once immediately after it is
+                  registered, we expect this.  Ignore it, and wait for the
+                  hotplug scripts. */
                return;
-       if (err < 0) {
-               xenbus_dev_error(dev, err, "reading handle");
+       }
+       if (err != 1) {
+               xenbus_dev_fatal(dev, err, "reading handle");
                return;
        }
 
        if (be->netif == NULL) {
-               be->netif = alloc_netif(be->frontend_id, handle, be_mac);
+               u8 be_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+
+               be->netif = alloc_netif(dev->otherend_id, handle, be_mac);
                if (IS_ERR(be->netif)) {
                        err = PTR_ERR(be->netif);
                        be->netif = NULL;
-                       xenbus_dev_error(dev, err, "creating interface");
+                       xenbus_dev_fatal(dev, err, "creating interface");
                        return;
                }
 
                kobject_hotplug(&dev->dev.kobj, KOBJ_ONLINE);
 
-               /* Pass in NULL node to skip exist test. */
-               frontend_changed(&be->watch, NULL, 0);
+               maybe_connect(be);
        }
 }
 
-static int netback_hotplug(struct xenbus_device *xdev, char **envp,
-                          int num_envp, char *buffer, int buffer_size)
+
+/**
+ * Callback received when the frontend's state changes.
+ */
+static void frontend_changed(struct xenbus_device *dev,
+                            XenbusState frontend_state)
 {
-       struct backend_info *be = xdev->data;
-       netif_t *netif = be->netif;
-       int i = 0, length = 0;
+       struct backend_info *be = dev->data;
 
-       char *val = xenbus_read(NULL, xdev->nodename, "script", NULL);
-       if (IS_ERR(val)) {
-               int err = PTR_ERR(val);
-               xenbus_dev_error(xdev, err, "reading script");
-               return err;
-       }
-       else {
-               add_hotplug_env_var(envp, num_envp, &i,
-                                   buffer, buffer_size, &length,
-                                   "script=%s", val);
-               kfree(val);
+       DPRINTK("");
+
+       be->frontend_state = frontend_state;
+
+       switch (frontend_state) {
+       case XenbusStateInitialising:
+       case XenbusStateInitialised:
+               break;
+
+       case XenbusStateConnected:
+               maybe_connect(be);
+               break;
+
+       case XenbusStateClosing:
+               xenbus_switch_state(dev, NULL, XenbusStateClosing);
+               break;
+
+       case XenbusStateClosed:
+               device_unregister(&be->dev->dev);
+               break;
+
+       case XenbusStateUnknown:
+       case XenbusStateInitWait:
+       default:
+               xenbus_dev_fatal(be->dev, -EINVAL, "saw state %d at frontend",
+                                frontend_state);
+               break;
        }
+}
 
-       add_hotplug_env_var(envp, num_envp, &i,
-                           buffer, buffer_size, &length,
-                           "vif=%s", netif->dev->name);
 
-       envp[i] = NULL;
+/* ** Connection ** */
 
-       return 0;
+
+static void maybe_connect(struct backend_info *be)
+{
+       if (be->netif != NULL && be->frontend_state == XenbusStateConnected) {
+               connect(be);
+       }
 }
 
-static int netback_probe(struct xenbus_device *dev,
-                        const struct xenbus_device_id *id)
+
+static void connect(struct backend_info *be)
 {
-       struct backend_info *be;
-       char *frontend;
        int err;
+       struct xenbus_device *dev = be->dev;
 
-       be = kmalloc(sizeof(*be), GFP_KERNEL);
-       if (!be) {
-               xenbus_dev_error(dev, -ENOMEM, "allocating backend structure");
-               return -ENOMEM;
-       }
-       memset(be, 0, sizeof(*be));
+       err = connect_rings(be);
+       if (err)
+               return;
 
-       frontend = NULL;
-       err = xenbus_gather(NULL, dev->nodename,
-                           "frontend-id", "%li", &be->frontend_id,
-                           "frontend", NULL, &frontend,
-                           NULL);
-       if (XENBUS_EXIST_ERR(err))
-               goto free_be;
-       if (err < 0) {
-               xenbus_dev_error(dev, err,
-                                "reading %s/frontend or frontend-id",
-                                dev->nodename);
-               goto free_be;
-       }
-       if (strlen(frontend) == 0 || !xenbus_exists(NULL, frontend, "")) {
-               /* If we can't get a frontend path and a frontend-id,
-                * then our bus-id is no longer valid and we need to
-                * destroy the backend device.
-                */
-               err = -ENOENT;
-               goto free_be;
+       err = xen_net_read_mac(dev, be->netif->fe_dev_addr);
+       if (err) {
+               xenbus_dev_fatal(dev, err, "parsing %s/mac", dev->nodename);
+               return;
        }
 
-       be->dev = dev;
-       be->backend_watch.node = dev->nodename;
-       be->backend_watch.callback = backend_changed;
-       /* Registration implicitly calls backend_changed. */
-       err = register_xenbus_watch(&be->backend_watch);
+       xenbus_switch_state(dev, NULL, XenbusStateConnected);
+}
+
+
+static int connect_rings(struct backend_info *be)
+{
+       struct xenbus_device *dev = be->dev;
+       unsigned long tx_ring_ref, rx_ring_ref;
+       unsigned int evtchn;
+       int err;
+
+       DPRINTK("");
+
+       err = xenbus_gather(NULL, dev->otherend,
+                           "tx-ring-ref", "%lu", &tx_ring_ref,
+                           "rx-ring-ref", "%lu", &rx_ring_ref,
+                           "event-channel", "%u", &evtchn, NULL);
        if (err) {
-               be->backend_watch.node = NULL;
-               xenbus_dev_error(dev, err, "adding backend watch on %s",
-                                dev->nodename);
-               goto free_be;
+               xenbus_dev_fatal(dev, err,
+                                "reading %s/ring-ref and event-channel",
+                                dev->otherend);
+               return err;
        }
 
-       be->frontpath = frontend;
-       be->watch.node = be->frontpath;
-       be->watch.callback = frontend_changed;
-       err = register_xenbus_watch(&be->watch);
+       /* Map the shared frame, irq etc. */
+       err = netif_map(be->netif, tx_ring_ref, rx_ring_ref, evtchn);
        if (err) {
-               be->watch.node = NULL;
-               xenbus_dev_error(dev, err,
-                                "adding frontend watch on %s",
-                                be->frontpath);
-               goto free_be;
+               xenbus_dev_fatal(dev, err,
+                                "mapping shared-frames %lu/%lu port %u",
+                                tx_ring_ref, rx_ring_ref, evtchn);
+               return err;
        }
-
-       dev->data = be;
        return 0;
-
- free_be:
-       if (be->backend_watch.node)
-               unregister_xenbus_watch(&be->backend_watch);
-       kfree(frontend);
-       kfree(be);
-       return err;
 }
 
+
+/* ** Driver Registration ** */
+
+
 static struct xenbus_device_id netback_ids[] = {
        { "vif" },
        { "" }
 };
 
+
 static struct xenbus_driver netback = {
        .name = "vif",
        .owner = THIS_MODULE,
@@ -269,13 +306,16 @@ static struct xenbus_driver netback = {
        .probe = netback_probe,
        .remove = netback_remove,
        .hotplug = netback_hotplug,
+       .otherend_changed = frontend_changed,
 };
 
+
 void netif_xenbus_init(void)
 {
        xenbus_register_backend(&netback);
 }
 
+
 /*
  * Local variables:
  *  c-file-style: "linux"
index 30486a426d6ab0bf33a1492b982f205c5fc252e4..2b6fad55a7989b27a3140323e910cb28e08ce651 100644 (file)
@@ -2,6 +2,7 @@
  * Virtual network driver for conversing with remote driver backends.
  * 
  * Copyright (c) 2002-2005, K A Fraser
+ * Copyright (c) 2005, XenSource Ltd
  * 
  * This file may be distributed separately from the Linux kernel, or
  * incorporated into other software packages, subject to the following license:
@@ -56,6 +57,7 @@
 #include <asm/uaccess.h>
 #include <asm-xen/xen-public/grant_table.h>
 #include <asm-xen/gnttab.h>
+#include <asm-xen/net_driver_util.h>
 
 #define GRANT_INVALID_REF      0
 
 #define TX_TEST_IDX req_cons  /* conservative: not seen all our requests? */
 #endif
 
-
-static void network_tx_buf_gc(struct net_device *dev);
-static void network_alloc_rx_buffers(struct net_device *dev);
-
 static unsigned long rx_pfn_array[NETIF_RX_RING_SIZE];
 static multicall_entry_t rx_mcl[NETIF_RX_RING_SIZE+1];
 static mmu_update_t rx_mmu[NETIF_RX_RING_SIZE];
 
-#ifdef CONFIG_PROC_FS
-static int xennet_proc_init(void);
-static int xennet_proc_addif(struct net_device *dev);
-static void xennet_proc_delif(struct net_device *dev);
-#else
-#define xennet_proc_init()   (0)
-#define xennet_proc_addif(d) (0)
-#define xennet_proc_delif(d) ((void)0)
-#endif
-
-#define netfront_info net_private
-struct net_private
+struct netfront_info
 {
        struct list_head list;
        struct net_device *netdev;
@@ -154,9 +141,6 @@ struct net_private
        grant_ref_t grant_rx_ref[NETIF_TX_RING_SIZE + 1]; 
 
        struct xenbus_device *xbdev;
-       char *backend;
-       int backend_id;
-       struct xenbus_watch watch;
        int tx_ring_ref;
        int rx_ring_ref;
        u8 mac[ETH_ALEN];
@@ -181,16 +165,256 @@ static char *be_state_name[] = {
 
 #ifdef DEBUG
 #define DPRINTK(fmt, args...) \
-       printk(KERN_ALERT "xen_net (%s:%d) " fmt, __FUNCTION__, __LINE__, ##args)
+       printk(KERN_ALERT "netfront (%s:%d) " fmt, __FUNCTION__, __LINE__, ##args)
 #else
 #define DPRINTK(fmt, args...) ((void)0)
 #endif
 #define IPRINTK(fmt, args...) \
-       printk(KERN_INFO "xen_net: " fmt, ##args)
+       printk(KERN_INFO "netfront: " fmt, ##args)
 #define WPRINTK(fmt, args...) \
-       printk(KERN_WARNING "xen_net: " fmt, ##args)
+       printk(KERN_WARNING "netfront: " fmt, ##args)
+
+
+static int talk_to_backend(struct xenbus_device *, struct netfront_info *);
+static int setup_device(struct xenbus_device *, struct netfront_info *);
+static int create_netdev(int, struct xenbus_device *, struct net_device **);
+
+static void netfront_closing(struct xenbus_device *);
+
+static void end_access(int, void *);
+static void netif_disconnect_backend(struct netfront_info *);
+static void close_netdev(struct netfront_info *);
+static void netif_free(struct netfront_info *);
+
+static void show_device(struct netfront_info *);
+
+static void network_connect(struct net_device *);
+static void network_tx_buf_gc(struct net_device *);
+static void network_alloc_rx_buffers(struct net_device *);
+static int send_fake_arp(struct net_device *);
+
+static irqreturn_t netif_int(int irq, void *dev_id, struct pt_regs *ptregs);
+
+#ifdef CONFIG_PROC_FS
+static int xennet_proc_init(void);
+static int xennet_proc_addif(struct net_device *dev);
+static void xennet_proc_delif(struct net_device *dev);
+#else
+#define xennet_proc_init()   (0)
+#define xennet_proc_addif(d) (0)
+#define xennet_proc_delif(d) ((void)0)
+#endif
+
+
+/**
+ * Entry point to this code when a new device is created.  Allocate the basic
+ * structures and the ring buffers for communication with the backend, and
+ * inform the backend of the appropriate details for those.  Switch to
+ * Connected state.
+ */
+static int netfront_probe(struct xenbus_device *dev,
+                         const struct xenbus_device_id *id)
+{
+       int err;
+       struct net_device *netdev;
+       struct netfront_info *info;
+       unsigned int handle;
+
+       err = xenbus_scanf(NULL, dev->nodename, "handle", "%u", &handle);
+       if (err != 1) {
+               xenbus_dev_fatal(dev, err, "reading handle");
+               return err;
+       }
+
+       err = create_netdev(handle, dev, &netdev);
+       if (err) {
+               xenbus_dev_fatal(dev, err, "creating netdev");
+               return err;
+       }
+
+       info = netdev_priv(netdev);
+       dev->data = info;
+
+       err = talk_to_backend(dev, info);
+       if (err) {
+               kfree(info);
+               dev->data = NULL;
+               return err;
+       }
+
+       return 0;
+}
+
+
+/**
+ * We are reconnecting to the backend, due to a suspend/resume, or a backend
+ * driver restart.  We tear down our netif structure and recreate it, but
+ * leave the device-layer structures intact so that this is transparent to the
+ * rest of the kernel.
+ */
+static int netfront_resume(struct xenbus_device *dev)
+{
+       struct netfront_info *info = dev->data;
+
+       DPRINTK("%s\n", dev->nodename);
+
+       netif_disconnect_backend(info);
+       return talk_to_backend(dev, info);
+}
+
+
+/* Common code used when first setting up, and when resuming. */
+static int talk_to_backend(struct xenbus_device *dev,
+                          struct netfront_info *info)
+{
+       const char *message;
+       struct xenbus_transaction *xbt;
+       int err;
+
+       err = xen_net_read_mac(dev, info->mac);
+       if (err) {
+               xenbus_dev_fatal(dev, err, "parsing %s/mac", dev->nodename);
+               goto out;
+       }
+
+       /* Create shared ring, alloc event channel. */
+       err = setup_device(dev, info);
+       if (err)
+               goto out;
+
+again:
+       xbt = xenbus_transaction_start();
+       if (IS_ERR(xbt)) {
+               xenbus_dev_fatal(dev, err, "starting transaction");
+               goto destroy_ring;
+       }
+
+       err = xenbus_printf(xbt, dev->nodename, "tx-ring-ref","%u",
+                           info->tx_ring_ref);
+       if (err) {
+               message = "writing tx ring-ref";
+               goto abort_transaction;
+       }
+       err = xenbus_printf(xbt, dev->nodename, "rx-ring-ref","%u",
+                           info->rx_ring_ref);
+       if (err) {
+               message = "writing rx ring-ref";
+               goto abort_transaction;
+       }
+       err = xenbus_printf(xbt, dev->nodename,
+                           "event-channel", "%u", info->evtchn);
+       if (err) {
+               message = "writing event-channel";
+               goto abort_transaction;
+       }
+
+       err = xenbus_printf(xbt, dev->nodename,
+                           "state", "%d", XenbusStateConnected);
+       if (err) {
+               message = "writing frontend XenbusStateConnected";
+               goto abort_transaction;
+       }
+
+       err = xenbus_transaction_end(xbt, 0);
+       if (err) {
+               if (err == -EAGAIN)
+                       goto again;
+               xenbus_dev_fatal(dev, err, "completing transaction");
+               goto destroy_ring;
+       }
+
+       return 0;
+
+ abort_transaction:
+       xenbus_transaction_end(xbt, 1);
+       xenbus_dev_fatal(dev, err, "%s", message);
+ destroy_ring:
+       netif_free(info);
+ out:
+       return err;
+}
+
+
+static int setup_device(struct xenbus_device *dev, struct netfront_info *info)
+{
+       int err;
+       struct net_device *netdev = info->netdev;
+
+       info->tx_ring_ref = GRANT_INVALID_REF;
+       info->rx_ring_ref = GRANT_INVALID_REF;
+       info->rx = NULL;
+       info->tx = NULL;
+       info->irq = 0;
+
+       info->tx = (netif_tx_interface_t *)__get_free_page(GFP_KERNEL);
+       if (!info->tx) {
+               err = -ENOMEM;
+               xenbus_dev_fatal(dev, err, "allocating tx ring page");
+               goto fail;
+       }
+       info->rx = (netif_rx_interface_t *)__get_free_page(GFP_KERNEL);
+       if (!info->rx) {
+               err = -ENOMEM;
+               xenbus_dev_fatal(dev, err, "allocating rx ring page");
+               goto fail;
+       }
+       memset(info->tx, 0, PAGE_SIZE);
+       memset(info->rx, 0, PAGE_SIZE);
+       info->backend_state = BEST_DISCONNECTED;
+
+       err = xenbus_grant_ring(dev, virt_to_mfn(info->tx));
+       if (err < 0)
+               goto fail;
+       info->tx_ring_ref = err;
+
+       err = xenbus_grant_ring(dev, virt_to_mfn(info->rx));
+       if (err < 0)
+               goto fail;
+       info->rx_ring_ref = err;
+
+       err = xenbus_alloc_evtchn(dev, &info->evtchn);
+       if (err)
+               goto fail;
+
+       memcpy(netdev->dev_addr, info->mac, ETH_ALEN);
+       network_connect(netdev);
+       info->irq = bind_evtchn_to_irqhandler(
+               info->evtchn, netif_int, SA_SAMPLE_RANDOM, netdev->name,
+               netdev);
+       (void)send_fake_arp(netdev);
+       show_device(info);
+
+       return 0;
+
+ fail:
+       netif_free(info);
+       return err;
+}
+
+
+/**
+ * Callback received when the backend's state changes.
+ */
+static void backend_changed(struct xenbus_device *dev,
+                           XenbusState backend_state)
+{
+       DPRINTK("\n");
+
+       switch (backend_state) {
+       case XenbusStateInitialising:
+       case XenbusStateInitWait:
+       case XenbusStateInitialised:
+       case XenbusStateConnected:
+       case XenbusStateUnknown:
+       case XenbusStateClosed:
+               break;
+
+       case XenbusStateClosing:
+               netfront_closing(dev);
+               break;
+       }
+}
 
-static void netif_free(struct netfront_info *info);
 
 /** Send a packet on a net device to encourage switches to learn the
  * MAC. We send a fake ARP request.
@@ -220,9 +444,10 @@ static int send_fake_arp(struct net_device *dev)
        return dev_queue_xmit(skb);
 }
 
+
 static int network_open(struct net_device *dev)
 {
-       struct net_private *np = netdev_priv(dev);
+       struct netfront_info *np = netdev_priv(dev);
 
        memset(&np->stats, 0, sizeof(np->stats));
 
@@ -240,7 +465,7 @@ static void network_tx_buf_gc(struct net_device *dev)
 {
        NETIF_RING_IDX i, prod;
        unsigned short id;
-       struct net_private *np = netdev_priv(dev);
+       struct netfront_info *np = netdev_priv(dev);
        struct sk_buff *skb;
 
        if (np->backend_state != BEST_CONNECTED)
@@ -295,7 +520,7 @@ static void network_tx_buf_gc(struct net_device *dev)
 static void network_alloc_rx_buffers(struct net_device *dev)
 {
        unsigned short id;
-       struct net_private *np = netdev_priv(dev);
+       struct netfront_info *np = netdev_priv(dev);
        struct sk_buff *skb;
        int i, batch_target;
        NETIF_RING_IDX req_prod = np->rx->req_prod;
@@ -337,7 +562,8 @@ static void network_alloc_rx_buffers(struct net_device *dev)
                ref = gnttab_claim_grant_reference(&np->gref_rx_head);
                BUG_ON((signed short)ref < 0);
                np->grant_rx_ref[id] = ref;
-               gnttab_grant_foreign_transfer_ref(ref, np->backend_id);
+               gnttab_grant_foreign_transfer_ref(ref,
+                                                 np->xbdev->otherend_id);
                np->rx->ring[MASK_NETIF_RX_IDX(req_prod + i)].req.gref = ref;
                rx_pfn_array[i] = virt_to_mfn(skb->head);
 
@@ -385,7 +611,7 @@ static void network_alloc_rx_buffers(struct net_device *dev)
 static int network_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        unsigned short id;
-       struct net_private *np = netdev_priv(dev);
+       struct netfront_info *np = netdev_priv(dev);
        netif_tx_request_t *tx;
        NETIF_RING_IDX i;
        grant_ref_t ref;
@@ -429,7 +655,7 @@ static int network_start_xmit(struct sk_buff *skb, struct net_device *dev)
        BUG_ON((signed short)ref < 0);
        mfn = virt_to_mfn(skb->data);
        gnttab_grant_foreign_access_ref(
-               ref, np->backend_id, mfn, GNTMAP_readonly);
+               ref, np->xbdev->otherend_id, mfn, GNTMAP_readonly);
        tx->gref = np->grant_tx_ref[id] = ref;
        tx->offset = (unsigned long)skb->data & ~PAGE_MASK;
        tx->size = skb->len;
@@ -466,7 +692,7 @@ static int network_start_xmit(struct sk_buff *skb, struct net_device *dev)
 static irqreturn_t netif_int(int irq, void *dev_id, struct pt_regs *ptregs)
 {
        struct net_device *dev = dev_id;
-       struct net_private *np = netdev_priv(dev);
+       struct netfront_info *np = netdev_priv(dev);
        unsigned long flags;
 
        spin_lock_irqsave(&np->tx_lock, flags);
@@ -483,7 +709,7 @@ static irqreturn_t netif_int(int irq, void *dev_id, struct pt_regs *ptregs)
 
 static int netif_poll(struct net_device *dev, int *pbudget)
 {
-       struct net_private *np = netdev_priv(dev);
+       struct netfront_info *np = netdev_priv(dev);
        struct sk_buff *skb, *nskb;
        netif_rx_response_t *rx;
        NETIF_RING_IDX i, rp;
@@ -534,7 +760,7 @@ static int netif_poll(struct net_device *dev, int *pbudget)
                if(ref == GRANT_INVALID_REF) { 
                        printk(KERN_WARNING "Bad rx grant reference %d "
                               "from dom %d.\n",
-                              ref, np->backend_id);
+                              ref, np->xbdev->otherend_id);
                        np->rx->ring[MASK_NETIF_RX_IDX(np->rx->req_prod)].
                                req.id = rx->id;
                        wmb();
@@ -678,7 +904,7 @@ static int netif_poll(struct net_device *dev, int *pbudget)
 
 static int network_close(struct net_device *dev)
 {
-       struct net_private *np = netdev_priv(dev);
+       struct netfront_info *np = netdev_priv(dev);
        np->user_state = UST_CLOSED;
        netif_stop_queue(np->netdev);
        return 0;
@@ -687,13 +913,13 @@ static int network_close(struct net_device *dev)
 
 static struct net_device_stats *network_get_stats(struct net_device *dev)
 {
-       struct net_private *np = netdev_priv(dev);
+       struct netfront_info *np = netdev_priv(dev);
        return &np->stats;
 }
 
 static void network_connect(struct net_device *dev)
 {
-       struct net_private *np;
+       struct netfront_info *np;
        int i, requeue_idx;
        netif_tx_request_t *tx;
        struct sk_buff *skb;
@@ -736,7 +962,7 @@ static void network_connect(struct net_device *dev)
 
                tx->id = i;
                gnttab_grant_foreign_access_ref(
-                       np->grant_tx_ref[i], np->backend_id, 
+                       np->grant_tx_ref[i], np->xbdev->otherend_id, 
                        virt_to_mfn(np->tx_skbs[i]->data),
                        GNTMAP_readonly); 
                tx->gref = np->grant_tx_ref[i];
@@ -755,7 +981,7 @@ static void network_connect(struct net_device *dev)
                if ((unsigned long)np->rx_skbs[i] < __PAGE_OFFSET)
                        continue;
                gnttab_grant_foreign_transfer_ref(
-                       np->grant_rx_ref[i], np->backend_id);
+                       np->grant_rx_ref[i], np->xbdev->otherend_id);
                np->rx->ring[requeue_idx].req.gref =
                        np->grant_rx_ref[i];
                np->rx->ring[requeue_idx].req.id = i;
@@ -782,7 +1008,7 @@ static void network_connect(struct net_device *dev)
        spin_unlock_irq(&np->tx_lock);
 }
 
-static void show_device(struct net_private *np)
+static void show_device(struct netfront_info *np)
 {
 #ifdef DEBUG
        if (np) {
@@ -799,27 +1025,9 @@ static void show_device(struct net_private *np)
 #endif
 }
 
-/*
- * Move the vif into connected state.
- * Sets the mac and event channel from the message.
- * Binds the irq to the event channel.
- */
-static void 
-connect_device(struct net_private *np, unsigned int evtchn)
-{
-       struct net_device *dev = np->netdev;
-       memcpy(dev->dev_addr, np->mac, ETH_ALEN);
-       np->evtchn = evtchn;
-       network_connect(dev);
-       np->irq = bind_evtchn_to_irqhandler(
-               np->evtchn, netif_int, SA_SAMPLE_RANDOM, dev->name, dev);
-       (void)send_fake_arp(dev);
-       show_device(np);
-}
-
 static void netif_uninit(struct net_device *dev)
 {
-       struct net_private *np = netdev_priv(dev);
+       struct netfront_info *np = netdev_priv(dev);
        gnttab_free_grant_references(np->gref_tx_head);
        gnttab_free_grant_references(np->gref_rx_head);
 }
@@ -840,9 +1048,9 @@ static int create_netdev(int handle, struct xenbus_device *dev,
 {
        int i, err = 0;
        struct net_device *netdev = NULL;
-       struct net_private *np = NULL;
+       struct netfront_info *np = NULL;
 
-       if ((netdev = alloc_etherdev(sizeof(struct net_private))) == NULL) {
+       if ((netdev = alloc_etherdev(sizeof(struct netfront_info))) == NULL) {
                printk(KERN_WARNING "%s> alloc_etherdev failed.\n",
                       __FUNCTION__);
                err = -ENOMEM;
@@ -929,15 +1137,6 @@ static int create_netdev(int handle, struct xenbus_device *dev,
        goto exit;
 }
 
-static int destroy_netdev(struct net_device *netdev)
-{
-#ifdef CONFIG_PROC_FS
-       xennet_proc_delif(netdev);
-#endif
-        unregister_netdev(netdev);
-       return 0;
-}
-
 /*
  * We use this notifier to send out a fake ARP reply to reset switches and
  * router ARP caches when an IP interface is brought up on a VIF.
@@ -955,91 +1154,70 @@ inetdev_notify(struct notifier_block *this, unsigned long event, void *ptr)
        return NOTIFY_DONE;
 }
 
-static struct notifier_block notifier_inetdev = {
-       .notifier_call  = inetdev_notify,
-       .next           = NULL,
-       .priority       = 0
-};
 
-static struct xenbus_device_id netfront_ids[] = {
-       { "vif" },
-       { "" }
-};
+/* ** Close down ** */
 
-static void watch_for_status(struct xenbus_watch *watch,
-                            const char **vec, unsigned int len)
-{
-}
 
-static int setup_device(struct xenbus_device *dev, struct netfront_info *info)
+/**
+ * Handle the change of state of the backend to Closing.  We must delete our
+ * device-layer structures now, to ensure that writes are flushed through to
+ * the backend.  Once is this done, we can switch to Closed in
+ * acknowledgement.
+ */
+static void netfront_closing(struct xenbus_device *dev)
 {
-       int err;
-       evtchn_op_t op = {
-               .cmd = EVTCHNOP_alloc_unbound,
-               .u.alloc_unbound.dom = DOMID_SELF,
-               .u.alloc_unbound.remote_dom = info->backend_id };
+       struct netfront_info *info = dev->data;
 
-       info->tx_ring_ref = GRANT_INVALID_REF;
-       info->rx_ring_ref = GRANT_INVALID_REF;
-       info->rx = NULL;
-       info->tx = NULL;
-       info->irq = 0;
+       DPRINTK("netfront_closing: %s removed\n", dev->nodename);
 
-       info->tx = (netif_tx_interface_t *)__get_free_page(GFP_KERNEL);
-       if (info->tx == 0) {
-               err = -ENOMEM;
-               xenbus_dev_error(dev, err, "allocating tx ring page");
-               goto out;
-       }
-       info->rx = (netif_rx_interface_t *)__get_free_page(GFP_KERNEL);
-       if (info->rx == 0) {
-               err = -ENOMEM;
-               xenbus_dev_error(dev, err, "allocating rx ring page");
-               goto out;
-       }
-       memset(info->tx, 0, PAGE_SIZE);
-       memset(info->rx, 0, PAGE_SIZE);
-       info->backend_state = BEST_DISCONNECTED;
+       close_netdev(info);
 
-       err = gnttab_grant_foreign_access(info->backend_id,
-                                         virt_to_mfn(info->tx), 0);
-       if (err < 0) {
-               xenbus_dev_error(dev, err, "granting access to tx ring page");
-               goto out;
-       }
-       info->tx_ring_ref = err;
+       xenbus_switch_state(dev, NULL, XenbusStateClosed);
+}
 
-       err = gnttab_grant_foreign_access(info->backend_id,
-                                         virt_to_mfn(info->rx), 0);
-       if (err < 0) {
-               xenbus_dev_error(dev, err, "granting access to rx ring page");
-               goto out;
-       }
-       info->rx_ring_ref = err;
 
-       err = HYPERVISOR_event_channel_op(&op);
-       if (err) {
-               xenbus_dev_error(dev, err, "allocating event channel");
-               goto out;
-       }
+static int netfront_remove(struct xenbus_device *dev)
+{
+       struct netfront_info *info = dev->data;
 
-       connect_device(info, op.u.alloc_unbound.port);
+       DPRINTK("%s\n", dev->nodename);
+
+       netif_free(info);
+       kfree(info);
 
        return 0;
+}
 
- out:
-       netif_free(info);
-       return err;
+
+static void netif_free(struct netfront_info *info)
+{
+       netif_disconnect_backend(info);
+       close_netdev(info);
 }
 
-static void end_access(int ref, void *page)
+
+static void close_netdev(struct netfront_info *info)
 {
-       if (ref != GRANT_INVALID_REF)
-               gnttab_end_foreign_access(ref, 0, (unsigned long)page);
+       if (info->netdev) {
+#ifdef CONFIG_PROC_FS
+               xennet_proc_delif(info->netdev);
+#endif
+               unregister_netdev(info->netdev);
+               info->netdev = NULL;
+       }
 }
 
-static void netif_free(struct netfront_info *info)
+
+static void netif_disconnect_backend(struct netfront_info *info)
 {
+       /* Stop old i/f to prevent errors whilst we rebuild the state. */
+       spin_lock_irq(&info->tx_lock);
+       spin_lock(&info->rx_lock);
+       netif_stop_queue(info->netdev);
+       /* info->backend_state = BEST_DISCONNECTED; */
+       spin_unlock(&info->rx_lock);
+       spin_unlock_irq(&info->tx_lock);
+    
        end_access(info->tx_ring_ref, info->tx);
        end_access(info->rx_ring_ref, info->rx);
        info->tx_ring_ref = GRANT_INVALID_REF;
@@ -1052,202 +1230,22 @@ static void netif_free(struct netfront_info *info)
        info->evtchn = info->irq = 0;
 }
 
-/* Stop network device and free tx/rx queues and irq. */
-static void shutdown_device(struct net_private *np)
-{
-       /* Stop old i/f to prevent errors whilst we rebuild the state. */
-       spin_lock_irq(&np->tx_lock);
-       spin_lock(&np->rx_lock);
-       netif_stop_queue(np->netdev);
-       /* np->backend_state = BEST_DISCONNECTED; */
-       spin_unlock(&np->rx_lock);
-       spin_unlock_irq(&np->tx_lock);
-    
-       /* Free resources. */
-       netif_free(np);
-}
-
-/* Common code used when first setting up, and when resuming. */
-static int talk_to_backend(struct xenbus_device *dev,
-                          struct netfront_info *info)
-{
-       char *backend, *mac, *e, *s;
-       const char *message;
-       struct xenbus_transaction *xbt;
-       int err, i;
-
-       backend = NULL;
-       err = xenbus_gather(NULL, dev->nodename,
-                           "backend-id", "%i", &info->backend_id,
-                           "backend", NULL, &backend,
-                           NULL);
-       if (XENBUS_EXIST_ERR(err))
-               goto out;
-       if (backend && strlen(backend) == 0) {
-               err = -ENOENT;
-               goto out;
-       }
-       if (err < 0) {
-               xenbus_dev_error(dev, err, "reading %s/backend or backend-id",
-                                dev->nodename);
-               goto out;
-       }
-
-       mac = xenbus_read(NULL, dev->nodename, "mac", NULL);
-       if (IS_ERR(mac)) {
-               err = PTR_ERR(mac);
-               xenbus_dev_error(dev, err, "reading %s/mac",
-                                dev->nodename);
-               goto out;
-       }
-       s = mac;
-       for (i = 0; i < ETH_ALEN; i++) {
-               info->mac[i] = simple_strtoul(s, &e, 16);
-               if (s == e || (e[0] != ':' && e[0] != 0)) {
-                       kfree(mac);
-                       err = -ENOENT;
-                       xenbus_dev_error(dev, err, "parsing %s/mac",
-                                        dev->nodename);
-                       goto out;
-               }
-               s = &e[1];
-       }
-       kfree(mac);
-
-       /* Create shared ring, alloc event channel. */
-       err = setup_device(dev, info);
-       if (err) {
-               xenbus_dev_error(dev, err, "setting up ring");
-               goto out;
-       }
-
-again:
-       xbt = xenbus_transaction_start();
-       if (IS_ERR(xbt)) {
-               xenbus_dev_error(dev, err, "starting transaction");
-               goto destroy_ring;
-       }
-
-       err = xenbus_printf(xbt, dev->nodename, "tx-ring-ref","%u",
-                           info->tx_ring_ref);
-       if (err) {
-               message = "writing tx ring-ref";
-               goto abort_transaction;
-       }
-       err = xenbus_printf(xbt, dev->nodename, "rx-ring-ref","%u",
-                           info->rx_ring_ref);
-       if (err) {
-               message = "writing rx ring-ref";
-               goto abort_transaction;
-       }
-       err = xenbus_printf(xbt, dev->nodename,
-                           "event-channel", "%u", info->evtchn);
-       if (err) {
-               message = "writing event-channel";
-               goto abort_transaction;
-       }
-
-       err = xenbus_transaction_end(xbt, 0);
-       if (err) {
-               if (err == -EAGAIN)
-                       goto again;
-               xenbus_dev_error(dev, err, "completing transaction");
-               goto destroy_ring;
-       }
-
-       info->watch.node = backend;
-       info->watch.callback = watch_for_status;
-       err = register_xenbus_watch(&info->watch);
-       if (err) {
-               message = "registering watch on backend";
-               goto destroy_ring;
-       }
-
-       info->backend = backend;
-
-       return 0;
 
- abort_transaction:
-       xenbus_transaction_end(xbt, 1);
-       xenbus_dev_error(dev, err, "%s", message);
- destroy_ring:
-       shutdown_device(info);
- out:
-       kfree(backend);
-       return err;
-}
-
-/*
- * Setup supplies the backend dir, virtual device.
- * We place an event channel and shared frame entries.
- * We watch backend to wait if it's ok.
- */
-static int netfront_probe(struct xenbus_device *dev,
-                         const struct xenbus_device_id *id)
+static void end_access(int ref, void *page)
 {
-       int err;
-       struct net_device *netdev;
-       struct netfront_info *info;
-       unsigned int handle;
-
-       err = xenbus_scanf(NULL, dev->nodename, "handle", "%u", &handle);
-       if (XENBUS_EXIST_ERR(err))
-               return err;
-       if (err < 0) {
-               xenbus_dev_error(dev, err, "reading handle");
-               return err;
-       }
-
-       err = create_netdev(handle, dev, &netdev);
-       if (err) {
-               xenbus_dev_error(dev, err, "creating netdev");
-               return err;
-       }
-
-       info = netdev_priv(netdev);
-       dev->data = info;
-
-       err = talk_to_backend(dev, info);
-       if (err) {
-               destroy_netdev(netdev);
-               kfree(netdev);
-               dev->data = NULL;
-               return err;
-       }
-
-       return 0;
+       if (ref != GRANT_INVALID_REF)
+               gnttab_end_foreign_access(ref, 0, (unsigned long)page);
 }
 
-static int netfront_remove(struct xenbus_device *dev)
-{
-       struct netfront_info *info = dev->data;
 
-       if (info->backend)
-               unregister_xenbus_watch(&info->watch);
+/* ** Driver registration ** */
 
-       netif_free(info);
 
-       kfree(info->backend);
-       kfree(info);
-
-       return 0;
-}
-
-static int netfront_suspend(struct xenbus_device *dev)
-{
-       struct netfront_info *info = dev->data;
-       unregister_xenbus_watch(&info->watch);
-       kfree(info->backend);
-       info->backend = NULL;
-       return 0;
-}
+static struct xenbus_device_id netfront_ids[] = {
+       { "vif" },
+       { "" }
+};
 
-static int netfront_resume(struct xenbus_device *dev)
-{
-       struct netfront_info *info = dev->data;
-       netif_free(info);
-       return talk_to_backend(dev, info);
-}
 
 static struct xenbus_driver netfront = {
        .name = "vif",
@@ -1256,13 +1254,15 @@ static struct xenbus_driver netfront = {
        .probe = netfront_probe,
        .remove = netfront_remove,
        .resume = netfront_resume,
-       .suspend = netfront_suspend,
+       .otherend_changed = backend_changed,
 };
 
-static void __init init_net_xenbus(void)
-{
-       xenbus_register_driver(&netfront);
-}
+
+static struct notifier_block notifier_inetdev = {
+       .notifier_call  = inetdev_notify,
+       .next           = NULL,
+       .priority       = 0
+};
 
 static int __init netif_init(void)
 {
@@ -1278,14 +1278,24 @@ static int __init netif_init(void)
 
        (void)register_inetaddr_notifier(&notifier_inetdev);
 
-       init_net_xenbus();
-
-       return err;
+       return xenbus_register_frontend(&netfront);
 }
+module_init(netif_init);
+
 
 static void netif_exit(void)
 {
+       unregister_inetaddr_notifier(&notifier_inetdev);
+
+       return xenbus_unregister_driver(&netfront);
 }
+module_exit(netif_exit);
+
+MODULE_LICENSE("BSD");
+/* ** /proc **/
+
 
 #ifdef CONFIG_PROC_FS
 
@@ -1298,7 +1308,7 @@ static int xennet_proc_read(
 {
        struct net_device *dev =
                (struct net_device *)((unsigned long)data & ~3UL);
-       struct net_private *np = netdev_priv(dev);
+       struct netfront_info *np = netdev_priv(dev);
        int len = 0, which_target = (long)data & 3;
     
        switch (which_target)
@@ -1324,7 +1334,7 @@ static int xennet_proc_write(
 {
        struct net_device *dev =
                (struct net_device *)((unsigned long)data & ~3UL);
-       struct net_private *np = netdev_priv(dev);
+       struct netfront_info *np = netdev_priv(dev);
        int which_target = (long)data & 3;
        char string[64];
        long target;
@@ -1438,8 +1448,6 @@ static void xennet_proc_delif(struct net_device *dev)
 
 #endif
 
-module_init(netif_init);
-module_exit(netif_exit);
 
 /*
  * Local variables:
index 075cb3ff43e44ed2f857fd4824f2dfdafb082a24..b07cb721393551660f8bfdbcc3aa55eccac49006 100644 (file)
@@ -1,6 +1,7 @@
 obj-y  += xenbus.o
 
 xenbus-objs =
+xenbus-objs += xenbus_client.o 
 xenbus-objs += xenbus_comms.o
 xenbus-objs += xenbus_xs.o
 xenbus-objs += xenbus_probe.o 
diff --git a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_client.c b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_client.c
new file mode 100644 (file)
index 0000000..869ce26
--- /dev/null
@@ -0,0 +1,253 @@
+/******************************************************************************
+ * Client-facing interface for the Xenbus driver.  In other words, the
+ * interface between the Xenbus and the device-specific code, be it the
+ * frontend or the backend of that driver.
+ *
+ * Copyright (C) 2005 XenSource Ltd
+ * 
+ * This file may be distributed separately from the Linux kernel, or
+ * incorporated into other software packages, subject to the following license:
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+
+#if 0
+#define DPRINTK(fmt, args...) \
+    printk("xenbus_client (%s:%d) " fmt ".\n", __FUNCTION__, __LINE__, ##args)
+#else
+#define DPRINTK(fmt, args...) ((void)0)
+#endif
+
+
+#include <asm-xen/evtchn.h>
+#include <asm-xen/gnttab.h>
+#include <asm-xen/xenbus.h>
+
+
+int xenbus_watch_path(struct xenbus_device *dev, const char *path,
+                     struct xenbus_watch *watch, 
+                     void (*callback)(struct xenbus_watch *,
+                                      const char **, unsigned int))
+{
+       int err;
+
+       watch->node = path;
+       watch->callback = callback;
+
+       err = register_xenbus_watch(watch);
+
+       if (err) {
+               watch->node = NULL;
+               watch->callback = NULL;
+               xenbus_dev_fatal(dev, err, "adding watch on %s", path);
+       }
+
+       return err;
+}
+EXPORT_SYMBOL(xenbus_watch_path);
+
+
+int xenbus_watch_path2(struct xenbus_device *dev, const char *path,
+                      const char *path2, struct xenbus_watch *watch, 
+                      void (*callback)(struct xenbus_watch *,
+                                       const char **, unsigned int))
+{
+       int err;
+       char *state =
+               kmalloc(strlen(path) + 1 + strlen(path2) + 1, GFP_KERNEL);
+       if (!state) {
+               xenbus_dev_fatal(dev, -ENOMEM, "allocating path for watch");
+               return -ENOMEM;
+       }
+       strcpy(state, path);
+       strcat(state, "/");
+       strcat(state, path2);
+
+       err = xenbus_watch_path(dev, state, watch, callback);
+
+       if (err) {
+               kfree(state);
+       }
+       return err;
+}
+EXPORT_SYMBOL(xenbus_watch_path2);
+
+
+int xenbus_switch_state(struct xenbus_device *dev,
+                       struct xenbus_transaction *xbt,
+                       XenbusState state)
+{
+       /* We check whether the state is currently set to the given value, and
+          if not, then the state is set.  We don't want to unconditionally
+          write the given state, because we don't want to fire watches
+          unnecessarily.
+        */
+
+       int current_state;
+
+       int err = xenbus_scanf(xbt, dev->nodename, "state", "%d",
+                              &current_state);
+       if (err == 1 && (XenbusState)current_state == state)
+               return 0;
+
+       err = xenbus_printf(xbt, dev->nodename, "state", "%d", state);
+       if (err) {
+               xenbus_dev_fatal(dev, err, "writing new state");
+               return err;
+       }
+       return 0;
+}
+EXPORT_SYMBOL(xenbus_switch_state);
+
+
+/**
+ * Return the path to the error node for the given device, or NULL on failure.
+ * If the value returned is non-NULL, then it is the caller's to kfree.
+ */
+static char *error_path(struct xenbus_device *dev)
+{
+       char *path_buffer = kmalloc(strlen("error/") + strlen(dev->nodename) +
+                                   1, GFP_KERNEL);
+       if (path_buffer == NULL) {
+               return NULL;
+       }
+
+       strcpy(path_buffer, "error/");
+       strcpy(path_buffer + strlen("error/"), dev->nodename);
+
+       return path_buffer;
+}
+
+
+void _dev_error(struct xenbus_device *dev, int err, const char *fmt,
+               va_list ap)
+{
+       int ret;
+       unsigned int len;
+       char *printf_buffer = NULL, *path_buffer = NULL;
+
+#define PRINTF_BUFFER_SIZE 4096
+       printf_buffer = kmalloc(PRINTF_BUFFER_SIZE, GFP_KERNEL);
+       if (printf_buffer == NULL)
+               goto fail;
+
+       len = sprintf(printf_buffer, "%i ", -err);
+       ret = vsnprintf(printf_buffer+len, PRINTF_BUFFER_SIZE-len, fmt, ap);
+
+       BUG_ON(len + ret > PRINTF_BUFFER_SIZE-1);
+       dev->has_error = 1;
+
+       path_buffer = error_path(dev);
+
+       if (path_buffer == NULL) {
+               printk("xenbus: failed to write error node for %s (%s)\n",
+                      dev->nodename, printf_buffer);
+               goto fail;
+       }
+
+       if (xenbus_write(NULL, path_buffer, "error", printf_buffer) != 0) {
+               printk("xenbus: failed to write error node for %s (%s)\n",
+                      dev->nodename, printf_buffer);
+               goto fail;
+       }
+
+fail:
+       if (printf_buffer)
+               kfree(printf_buffer);
+       if (path_buffer)
+               kfree(path_buffer);
+}
+
+
+void xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt,
+                     ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       _dev_error(dev, err, fmt, ap);
+       va_end(ap);
+}
+EXPORT_SYMBOL(xenbus_dev_error);
+
+
+void xenbus_dev_fatal(struct xenbus_device *dev, int err, const char *fmt,
+                     ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       _dev_error(dev, err, fmt, ap);
+       va_end(ap);
+       
+       xenbus_switch_state(dev, NULL, XenbusStateClosing);
+}
+EXPORT_SYMBOL(xenbus_dev_fatal);
+
+
+int xenbus_grant_ring(struct xenbus_device *dev, unsigned long ring_mfn)
+{
+       int err = gnttab_grant_foreign_access(dev->otherend_id, ring_mfn, 0);
+       if (err < 0)
+               xenbus_dev_fatal(dev, err, "granting access to ring page");
+       return err;
+}
+EXPORT_SYMBOL(xenbus_grant_ring);
+
+
+int xenbus_alloc_evtchn(struct xenbus_device *dev, int *port)
+{
+       evtchn_op_t op = {
+               .cmd = EVTCHNOP_alloc_unbound,
+               .u.alloc_unbound.dom = DOMID_SELF,
+               .u.alloc_unbound.remote_dom = dev->otherend_id };
+
+       int err = HYPERVISOR_event_channel_op(&op);
+       if (err)
+               xenbus_dev_fatal(dev, err, "allocating event channel");
+       else
+               *port = op.u.alloc_unbound.port;
+       return err;
+}
+EXPORT_SYMBOL(xenbus_alloc_evtchn);
+
+
+XenbusState xenbus_read_driver_state(const char *path)
+{
+       XenbusState result;
+
+       int err = xenbus_gather(NULL, path, "state", "%d", &result, NULL);
+       if (err)
+               result = XenbusStateClosed;
+
+       return result;
+}
+EXPORT_SYMBOL(xenbus_read_driver_state);
+
+
+/*
+ * Local variables:
+ *  c-file-style: "linux"
+ *  indent-tabs-mode: t
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */
index dda10a04adff3d87d6ab2336a53de4afe45d6987..5d5093d6c306ab8021fad1d4ddc3949d9dad18b5 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2005 Rusty Russell, IBM Corporation
  * Copyright (C) 2005 Mike Wray, Hewlett-Packard
+ * Copyright (C) 2005 XenSource Ltd
  * 
  * This file may be distributed separately from the Linux kernel, or
  * incorporated into other software packages, subject to the following license:
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  * IN THE SOFTWARE.
  */
-#define DEBUG
+
+#if 0
+#define DPRINTK(fmt, args...) \
+    printk("xenbus_probe (%s:%d) " fmt ".\n", __FUNCTION__, __LINE__, ##args)
+#else
+#define DPRINTK(fmt, args...) ((void)0)
+#endif
 
 #include <linux/kernel.h>
 #include <linux/err.h>
@@ -85,6 +92,7 @@ struct xen_bus_type
        struct device dev;
 };
 
+
 /* device/<type>/<id> => <type>-<id> */
 static int frontend_bus_id(char bus_id[BUS_ID_SIZE], const char *nodename)
 {
@@ -103,6 +111,62 @@ static int frontend_bus_id(char bus_id[BUS_ID_SIZE], const char *nodename)
        return 0;
 }
 
+
+static int read_otherend_details(struct xenbus_device *xendev,
+                                char *id_node, char *path_node)
+{
+       int err = xenbus_gather(NULL, xendev->nodename,
+                               id_node, "%i", &xendev->otherend_id,
+                               path_node, NULL, &xendev->otherend,
+                               NULL);
+       if (err) {
+               xenbus_dev_fatal(xendev, err,
+                                "reading other end details from %s",
+                                xendev->nodename);
+               return err;
+       }
+       if (strlen(xendev->otherend) == 0 ||
+           !xenbus_exists(NULL, xendev->otherend, "")) {
+               xenbus_dev_fatal(xendev, -ENOENT, "missing other end from %s",
+                                xendev->nodename);
+               kfree(xendev->otherend);
+               xendev->otherend = NULL;
+               return -ENOENT;
+       }
+
+       return 0;
+}
+
+
+static int read_backend_details(struct xenbus_device *xendev)
+{
+       return read_otherend_details(xendev, "backend-id", "backend");
+}
+
+
+static int read_frontend_details(struct xenbus_device *xendev)
+{
+       return read_otherend_details(xendev, "frontend-id", "frontend");
+}
+
+
+static void free_otherend_details(struct xenbus_device *dev)
+{
+       kfree(dev->otherend);
+       dev->otherend = NULL;
+}
+
+
+static void free_otherend_watch(struct xenbus_device *dev)
+{
+       if (dev->otherend_watch.node) {
+               unregister_xenbus_watch(&dev->otherend_watch);
+               kfree(dev->otherend_watch.node);
+               dev->otherend_watch.node = NULL;
+       }
+}
+
+
 /* Bus type for frontend drivers. */
 static int xenbus_probe_frontend(const char *type, const char *name);
 static struct xen_bus_type xenbus_frontend = {
@@ -165,6 +229,8 @@ static int xenbus_hotplug_backend(struct device *dev, char **envp,
        int i = 0;
        int length = 0;
 
+       DPRINTK("");
+
        if (dev == NULL)
                return -ENODEV;
 
@@ -211,20 +277,86 @@ static struct xen_bus_type xenbus_backend = {
        },
 };
 
+
+static void otherend_changed(struct xenbus_watch *watch,
+                            const char **vec, unsigned int len)
+{
+       struct xenbus_device *dev =
+               container_of(watch, struct xenbus_device, otherend_watch);
+       struct xenbus_driver *drv = to_xenbus_driver(dev->dev.driver);
+
+       /* Protect us against watches firing on old details when the otherend
+          details change, say immediately after a resume. */
+       if (!dev->otherend ||
+           strncmp(dev->otherend, vec[XS_WATCH_PATH],
+                   strlen(dev->otherend))) {
+               DPRINTK("Ignoring watch at %s", vec[XS_WATCH_PATH]);
+               return;
+       }
+
+       XenbusState state = xenbus_read_driver_state(dev->otherend);
+
+       DPRINTK("state is %d, %s, %s", state, dev->otherend_watch.node, vec[XS_WATCH_PATH]);
+
+       drv->otherend_changed(dev, state);
+}
+
+
+static int talk_to_otherend(struct xenbus_device *dev)
+{
+       struct xenbus_driver *drv = to_xenbus_driver(dev->dev.driver);
+       int err;
+
+       free_otherend_watch(dev);
+       free_otherend_details(dev);
+
+       err = drv->read_otherend_details(dev);
+       if (err)
+               return err;
+
+       return xenbus_watch_path2(dev, dev->otherend, "state",
+                                 &dev->otherend_watch, otherend_changed);
+}
+
+
 static int xenbus_dev_probe(struct device *_dev)
 {
        struct xenbus_device *dev = to_xenbus_device(_dev);
        struct xenbus_driver *drv = to_xenbus_driver(_dev->driver);
        const struct xenbus_device_id *id;
+       int err;
 
-       if (!drv->probe)
-               return -ENODEV;
+       DPRINTK("");
+
+       err = talk_to_otherend(dev);
+       if (err) {
+               printk(KERN_WARNING
+                      "xenbus_probe: talk_to_otherend on %s failed.\n",
+                      dev->nodename);
+               return err;
+       }
+
+       if (!drv->probe) {
+               err = -ENODEV;
+               goto fail;
+       }
 
        id = match_device(drv->ids, dev);
-       if (!id)
-               return -ENODEV;
+       if (!id) {
+               err = -ENODEV;
+               goto fail;
+       }
+
+       err = drv->probe(dev, id);
+       if (err)
+               goto fail;
 
-       return drv->probe(dev, id);
+       return 0;
+fail:
+       xenbus_dev_error(dev, err, "xenbus_dev_probe on %s", dev->nodename);
+       xenbus_switch_state(dev, NULL, XenbusStateClosed);
+       return -ENODEV;
+       
 }
 
 static int xenbus_dev_remove(struct device *_dev)
@@ -232,9 +364,16 @@ static int xenbus_dev_remove(struct device *_dev)
        struct xenbus_device *dev = to_xenbus_device(_dev);
        struct xenbus_driver *drv = to_xenbus_driver(_dev->driver);
 
-       if (!drv->remove)
-               return 0;
-       return drv->remove(dev);
+       DPRINTK("");
+
+       free_otherend_watch(dev);
+       free_otherend_details(dev);
+
+       if (drv->remove)
+               drv->remove(dev);
+
+       xenbus_switch_state(dev, NULL, XenbusStateClosed);
+       return 0;
 }
 
 static int xenbus_register_driver_common(struct xenbus_driver *drv,
@@ -254,16 +393,21 @@ static int xenbus_register_driver_common(struct xenbus_driver *drv,
        return ret;
 }
 
-int xenbus_register_driver(struct xenbus_driver *drv)
+int xenbus_register_frontend(struct xenbus_driver *drv)
 {
+       drv->read_otherend_details = read_backend_details;
+
        return xenbus_register_driver_common(drv, &xenbus_frontend);
 }
-EXPORT_SYMBOL(xenbus_register_driver);
+EXPORT_SYMBOL(xenbus_register_frontend);
 
 int xenbus_register_backend(struct xenbus_driver *drv)
 {
+       drv->read_otherend_details = read_frontend_details;
+
        return xenbus_register_driver_common(drv, &xenbus_backend);
 }
+EXPORT_SYMBOL(xenbus_register_backend);
 
 void xenbus_unregister_driver(struct xenbus_driver *drv)
 {
@@ -305,6 +449,8 @@ static int cleanup_dev(struct device *dev, void *data)
        struct xb_find_info *info = data;
        int len = strlen(info->nodename);
 
+       DPRINTK("%s", info->nodename);
+
        if (!strncmp(xendev->nodename, info->nodename, len)) {
                info->dev = xendev;
                get_device(dev);
@@ -327,12 +473,15 @@ static void xenbus_cleanup_devices(const char *path, struct bus_type *bus)
        } while (info.dev);
 }
 
-static void xenbus_release_device(struct device *dev)
+static void xenbus_dev_free(struct xenbus_device *xendev)
 {
-       if (dev) {
-               struct xenbus_device *xendev = to_xenbus_device(dev);
+       kfree(xendev);
+}
 
-               kfree(xendev);
+static void xenbus_dev_release(struct device *dev)
+{
+       if (dev) {
+               xenbus_dev_free(to_xenbus_device(dev));
        }
 }
 
@@ -369,13 +518,31 @@ static ssize_t xendev_show_devtype(struct device *dev, char *buf)
 }
 DEVICE_ATTR(devtype, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_devtype, NULL);
 
+
 static int xenbus_probe_node(struct xen_bus_type *bus,
                             const char *type,
                             const char *nodename)
 {
+#define CHECK_FAIL                             \
+       do {                                    \
+               if (err)                        \
+                       goto fail;              \
+       }                                       \
+       while (0)                               \
+
+
        int err;
        struct xenbus_device *xendev;
-       unsigned int stringlen;
+       size_t stringlen;
+       char *tmpstring;
+
+       XenbusState state = xenbus_read_driver_state(nodename);
+
+       if (state != XenbusStateInitialising) {
+               /* Device is not new, so ignore it.  This can happen if a
+                  device is going away after switching to Closed.  */
+               return 0;
+       }
 
        stringlen = strlen(nodename) + 1 + strlen(type) + 1;
        xendev = kmalloc(sizeof(*xendev) + stringlen, GFP_KERNEL);
@@ -384,31 +551,35 @@ static int xenbus_probe_node(struct xen_bus_type *bus,
        memset(xendev, 0, sizeof(*xendev));
 
        /* Copy the strings into the extra space. */
-       xendev->nodename = (char *)(xendev + 1);
-       strcpy(xendev->nodename, nodename);
-       xendev->devicetype = xendev->nodename + strlen(xendev->nodename) + 1;
-       strcpy(xendev->devicetype, type);
+
+       tmpstring = (char *)(xendev + 1);
+       strcpy(tmpstring, nodename);
+       xendev->nodename = tmpstring;
+
+       tmpstring += strlen(tmpstring) + 1;
+       strcpy(tmpstring, type);
+       xendev->devicetype = tmpstring;
 
        xendev->dev.parent = &bus->dev;
        xendev->dev.bus = &bus->bus;
-       xendev->dev.release = xenbus_release_device;
+       xendev->dev.release = xenbus_dev_release;
 
        err = bus->get_bus_id(xendev->dev.bus_id, xendev->nodename);
-       if (err) {
-               kfree(xendev);
-               return err;
-       }
+       CHECK_FAIL;
 
        /* Register with generic device framework. */
        err = device_register(&xendev->dev);
-       if (err) {
-               printk("XENBUS: Registering %s device %s: error %i\n",
-                      bus->bus.name, xendev->dev.bus_id, err);
-               kfree(xendev);
-       } else {
-               device_create_file(&xendev->dev, &dev_attr_nodename);
-               device_create_file(&xendev->dev, &dev_attr_devtype);
-       }
+       CHECK_FAIL;
+
+       device_create_file(&xendev->dev, &dev_attr_nodename);
+       device_create_file(&xendev->dev, &dev_attr_devtype);
+
+       return 0;
+
+#undef CHECK_FAIL
+
+fail:
+       xenbus_dev_free(xendev);
        return err;
 }
 
@@ -422,6 +593,8 @@ static int xenbus_probe_frontend(const char *type, const char *name)
        if (!nodename)
                return -ENOMEM;
        
+       DPRINTK("%s", nodename);
+
        err = xenbus_probe_node(&xenbus_frontend, type, nodename);
        kfree(nodename);
        return err;
@@ -439,6 +612,8 @@ static int xenbus_probe_backend_unit(const char *dir,
        if (!nodename)
                return -ENOMEM;
 
+       DPRINTK("%s\n", nodename);
+
        err = xenbus_probe_node(&xenbus_backend, type, nodename);
        kfree(nodename);
        return err;
@@ -452,6 +627,8 @@ static int xenbus_probe_backend(const char *type, const char *domid)
        char **dir;
        unsigned int i, dir_n = 0;
 
+       DPRINTK("");
+
        nodename = kasprintf("%s/%s/%s", xenbus_backend.root, type, domid);
        if (!nodename)
                return -ENOMEM;
@@ -574,12 +751,16 @@ static void dev_changed(const char *node, struct xen_bus_type *bus)
 static void frontend_changed(struct xenbus_watch *watch,
                             const char **vec, unsigned int len)
 {
+       DPRINTK("");
+
        dev_changed(vec[XS_WATCH_PATH], &xenbus_frontend);
 }
 
 static void backend_changed(struct xenbus_watch *watch,
                            const char **vec, unsigned int len)
 {
+       DPRINTK("");
+
        dev_changed(vec[XS_WATCH_PATH], &xenbus_backend);
 }
 
@@ -600,6 +781,8 @@ static int suspend_dev(struct device *dev, void *data)
        struct xenbus_driver *drv;
        struct xenbus_device *xdev;
 
+       DPRINTK("");
+
        if (dev->driver == NULL)
                return 0;
        drv = to_xenbus_driver(dev->driver);
@@ -607,33 +790,49 @@ static int suspend_dev(struct device *dev, void *data)
        if (drv->suspend)
                err = drv->suspend(xdev);
        if (err)
-               printk("xenbus: suspend %s failed: %i\n", dev->bus_id, err);
+               printk(KERN_WARNING
+                      "xenbus: suspend %s failed: %i\n", dev->bus_id, err);
        return 0;
 }
 
 static int resume_dev(struct device *dev, void *data)
 {
-       int err = 0;
+       int err;
        struct xenbus_driver *drv;
        struct xenbus_device *xdev;
 
+       DPRINTK("");
+
        if (dev->driver == NULL)
                return 0;
        drv = to_xenbus_driver(dev->driver);
        xdev = container_of(dev, struct xenbus_device, dev);
+
+       err = talk_to_otherend(xdev);
+       if (err) {
+               printk(KERN_WARNING
+                      "xenbus: resume (talk_to_otherend) %s failed: %i\n",
+                      dev->bus_id, err);
+               return err;
+       }
+
        if (drv->resume)
                err = drv->resume(xdev);
        if (err)
-               printk("xenbus: resume %s failed: %i\n", dev->bus_id, err);
-       return 0;
+               printk(KERN_WARNING
+                      "xenbus: resume %s failed: %i\n", dev->bus_id, err);
+       return err;
 }
 
 void xenbus_suspend(void)
 {
+       DPRINTK("");
+
        bus_for_each_dev(&xenbus_frontend.bus, NULL, NULL, suspend_dev);
        bus_for_each_dev(&xenbus_backend.bus, NULL, NULL, suspend_dev);
        xs_suspend();
 }
+EXPORT_SYMBOL(xenbus_suspend);
 
 void xenbus_resume(void)
 {
@@ -642,6 +841,7 @@ void xenbus_resume(void)
        bus_for_each_dev(&xenbus_frontend.bus, NULL, NULL, resume_dev);
        bus_for_each_dev(&xenbus_backend.bus, NULL, NULL, resume_dev);
 }
+EXPORT_SYMBOL(xenbus_resume);
 
 
 /* A flag to determine if xenstored is 'ready' (i.e. has started) */
@@ -683,8 +883,6 @@ void xenbus_probe(void *unused)
 
        /* Notify others that xenstore is up */
        notifier_call_chain(&xenstore_chain, 0, 0);
-
-       return;
 }
 
 
@@ -716,10 +914,10 @@ static int __init xenbus_probe_init(void)
 {
        int err = 0, dom0;
 
-       printk("xenbus_probe_init\n");
+       DPRINTK("");
 
        if (xen_init() < 0) {
-               printk("xen_init failed\n");
+               DPRINTK("failed");
                return -ENODEV;
        }
 
@@ -773,7 +971,8 @@ static int __init xenbus_probe_init(void)
        /* Initialize the interface to xenstore. */
        err = xs_init(); 
        if (err) {
-               printk("XENBUS: Error initializing xenstore comms: %i\n", err);
+               printk(KERN_WARNING
+                      "XENBUS: Error initializing xenstore comms: %i\n", err);
                return err; 
        }
 
index a646a3d3018dad2e2c64d64d8e9a567b8df2e045..36bb3d7f850c35bfda20f22b5d0e574185ee8bed 100644 (file)
@@ -517,87 +517,6 @@ int xenbus_printf(struct xenbus_transaction *t,
 }
 EXPORT_SYMBOL(xenbus_printf);
 
-/**
- * Return the path to the error node for the given device, or NULL on failure.
- * If the value returned is non-NULL, then it is the caller's to kfree.
- */
-static char *error_path(struct xenbus_device *dev)
-{
-       char *path_buffer = kmalloc(strlen("error/") + strlen(dev->nodename) +
-                                   1, GFP_KERNEL);
-       if (path_buffer == NULL) {
-               return NULL;
-       }
-
-       strcpy(path_buffer, "error/");
-       strcpy(path_buffer + strlen("error/"), dev->nodename);
-
-       return path_buffer;
-}
-
-/* Report a (negative) errno into the store, with explanation. */
-void xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt, ...)
-{
-       va_list ap;
-       int ret;
-       unsigned int len;
-       char *printf_buffer = NULL, *path_buffer = NULL;
-
-       printf_buffer = kmalloc(PRINTF_BUFFER_SIZE, GFP_KERNEL);
-       if (printf_buffer == NULL)
-               goto fail;
-
-       len = sprintf(printf_buffer, "%i ", -err);
-       va_start(ap, fmt);
-       ret = vsnprintf(printf_buffer+len, PRINTF_BUFFER_SIZE-len, fmt, ap);
-       va_end(ap);
-
-       BUG_ON(len + ret > PRINTF_BUFFER_SIZE-1);
-       dev->has_error = 1;
-
-       path_buffer = error_path(dev);
-
-       if (path_buffer == NULL) {
-               printk("xenbus: failed to write error node for %s (%s)\n",
-                      dev->nodename, printf_buffer);
-               goto fail;
-       }
-
-       if (xenbus_write(NULL, path_buffer, "error", printf_buffer) != 0) {
-               printk("xenbus: failed to write error node for %s (%s)\n",
-                      dev->nodename, printf_buffer);
-               goto fail;
-       }
-
-fail:
-       kfree(printf_buffer);
-       kfree(path_buffer);
-}
-EXPORT_SYMBOL(xenbus_dev_error);
-
-/* Clear any error. */
-void xenbus_dev_ok(struct xenbus_device *dev)
-{
-       if (dev->has_error) {
-               char *path_buffer = error_path(dev);
-
-               if (path_buffer == NULL) {
-                       printk("xenbus: failed to clear error node for %s\n",
-                              dev->nodename);
-                       return;
-               }
-
-               if (xenbus_rm(NULL, path_buffer, "error") != 0)
-                       printk("xenbus: failed to clear error node for %s\n",
-                              dev->nodename);
-               else
-                       dev->has_error = 0;
-
-               kfree(path_buffer);
-       }
-}
-EXPORT_SYMBOL(xenbus_dev_ok);
-       
 /* Takes tuples of names, scanf-style args, and void **, NULL terminated. */
 int xenbus_gather(struct xenbus_transaction *t, const char *dir, ...)
 {
index 8717e4201efa2aa97bb9e456e040d21c64b02735..ddc46e198e30675f89cfe2b6d30729fe0c815faa 100644 (file)
 #include <asm/semaphore.h>
 #include <asm-xen/xen-public/io/xs_wire.h>
 
+/* Register callback to watch this node. */
+struct xenbus_watch
+{
+       struct list_head list;
+
+       /* Path being watched. */
+       const char *node;
+
+       /* Callback (executed in a process context with no locks held). */
+       void (*callback)(struct xenbus_watch *,
+                        const char **vec, unsigned int len);
+};
+
+
+/* The state of either end of the Xenbus, i.e. the current communication
+   status of initialisation across the bus.  States here imply nothing about
+   the state of the connection between the driver and the kernel's device
+   layers.  */
+typedef enum
+{
+  XenbusStateUnknown      = 0,
+  XenbusStateInitialising = 1,
+  XenbusStateInitWait     = 2,  /* Finished early initialisation, but waiting
+                                   for information from the peer or hotplug
+                                  scripts. */
+  XenbusStateInitialised  = 3,  /* Initialised and waiting for a connection
+                                  from the peer. */
+  XenbusStateConnected    = 4,
+  XenbusStateClosing      = 5,  /* The device is being closed due to an error
+                                  or an unplug event. */
+  XenbusStateClosed       = 6
+
+} XenbusState;
+
+
 /* A xenbus device. */
 struct xenbus_device {
-       char *devicetype;
-       char *nodename;
+       const char *devicetype;
+       const char *nodename;
+       const char *otherend;
+       int otherend_id;
+       struct xenbus_watch otherend_watch;
        struct device dev;
        int has_error;
        void *data;
@@ -62,11 +100,14 @@ struct xenbus_driver {
        const struct xenbus_device_id *ids;
        int (*probe)(struct xenbus_device *dev,
                     const struct xenbus_device_id *id);
+       void (*otherend_changed)(struct xenbus_device *dev,
+                                XenbusState backend_state);
        int (*remove)(struct xenbus_device *dev);
        int (*suspend)(struct xenbus_device *dev);
        int (*resume)(struct xenbus_device *dev);
        int (*hotplug)(struct xenbus_device *, char **, int, char *, int);
        struct device_driver driver;
+       int (*read_otherend_details)(struct xenbus_device *dev);
 };
 
 static inline struct xenbus_driver *to_xenbus_driver(struct device_driver *drv)
@@ -74,7 +115,7 @@ static inline struct xenbus_driver *to_xenbus_driver(struct device_driver *drv)
        return container_of(drv, struct xenbus_driver, driver);
 }
 
-int xenbus_register_driver(struct xenbus_driver *drv);
+int xenbus_register_frontend(struct xenbus_driver *drv);
 int xenbus_register_backend(struct xenbus_driver *drv);
 void xenbus_unregister_driver(struct xenbus_driver *drv);
 
@@ -108,25 +149,6 @@ int xenbus_printf(struct xenbus_transaction *t,
  * sprintf-style type string, and pointer. Returns 0 or errno.*/
 int xenbus_gather(struct xenbus_transaction *t, const char *dir, ...);
 
-/* Report a (negative) errno into the store, with explanation. */
-void xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt,...);
-
-/* Clear any error. */
-void xenbus_dev_ok(struct xenbus_device *dev);
-
-/* Register callback to watch this node. */
-struct xenbus_watch
-{
-       struct list_head list;
-
-       /* Path being watched. */
-       char *node;
-
-       /* Callback (executed in a process context with no locks held). */
-       void (*callback)(struct xenbus_watch *,
-                        const char **vec, unsigned int len);
-};
-
 /* notifer routines for when the xenstore comes up */
 int register_xenstore_notifier(struct notifier_block *nb);
 void unregister_xenstore_notifier(struct notifier_block *nb);
@@ -153,6 +175,89 @@ void xenbus_resume(void);
 
 #define XENBUS_EXIST_ERR(err) ((err) == -ENOENT || (err) == -ERANGE)
 
+
+/**
+ * Register a watch on the given path, using the given xenbus_watch structure
+ * for storage, and the given callback function as the callback.  Return 0 on
+ * success, or -errno on error.  On success, the given path will be saved as
+ * watch->node, and remains the caller's to free.  On error, watch->node will
+ * be NULL, the device will switch to XenbusStateClosing, and the error will
+ * be saved in the store.
+ */
+int xenbus_watch_path(struct xenbus_device *dev, const char *path,
+                     struct xenbus_watch *watch, 
+                     void (*callback)(struct xenbus_watch *,
+                                      const char **, unsigned int));
+
+
+/**
+ * Register a watch on the given path/path2, using the given xenbus_watch
+ * structure for storage, and the given callback function as the callback.
+ * Return 0 on success, or -errno on error.  On success, the watched path
+ * (path/path2) will be saved as watch->node, and becomes the caller's to
+ * kfree().  On error, watch->node will be NULL, so the caller has nothing to
+ * free, the device will switch to XenbusStateClosing, and the error will be
+ * saved in the store.
+ */
+int xenbus_watch_path2(struct xenbus_device *dev, const char *path,
+                      const char *path2, struct xenbus_watch *watch, 
+                      void (*callback)(struct xenbus_watch *,
+                                       const char **, unsigned int));
+
+
+/**
+ * Advertise in the store a change of the given driver to the given new_state.
+ * Perform the change inside the given transaction xbt.  xbt may be NULL, in
+ * which case this is performed inside its own transaction.  Return 0 on
+ * success, or -errno on error.  On error, the device will switch to
+ * XenbusStateClosing, and the error will be saved in the store.
+ */
+int xenbus_switch_state(struct xenbus_device *dev,
+                       struct xenbus_transaction *xbt,
+                       XenbusState new_state);
+
+
+/**
+ * Grant access to the given ring_mfn to the peer of the given device.  Return
+ * 0 on success, or -errno on error.  On error, the device will switch to
+ * XenbusStateClosing, and the error will be saved in the store.
+ */
+int xenbus_grant_ring(struct xenbus_device *dev, unsigned long ring_mfn);
+
+
+/**
+ * Allocate an event channel for the given xenbus_device, assigning the newly
+ * created local port to *port.  Return 0 on success, or -errno on error.  On
+ * error, the device will switch to XenbusStateClosing, and the error will be
+ * saved in the store.
+ */
+int xenbus_alloc_evtchn(struct xenbus_device *dev, int *port);
+
+
+/**
+ * Return the state of the driver rooted at the given store path, or
+ * XenbusStateClosed if no state can be read.
+ */
+XenbusState xenbus_read_driver_state(const char *path);
+
+
+/***
+ * Report the given negative errno into the store, along with the given
+ * formatted message.
+ */
+void xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt,
+                     ...);
+
+
+/***
+ * Equivalent to xenbus_dev_error(dev, err, fmt, args), followed by
+ * xenbus_switch_state(dev, NULL, XenbusStateClosing) to schedule an orderly
+ * closedown of this driver and its peer.
+ */
+void xenbus_dev_fatal(struct xenbus_device *dev, int err, const char *fmt,
+                     ...);
+
+
 #endif /* _ASM_XEN_XENBUS_H */
 
 /*
index 201c88120c7d032781608d281bb0e47ce6fc9fd9..51c59fdfff6319476da0fb727f7c91540048122c 100644 (file)
@@ -22,6 +22,7 @@ from xen.xend import sxp
 from xen.xend.XendClient import server
 from xen.xend.XendError import XendError
 from xen.xend.xenstore.xstransact import xstransact
+from xen.xend.server import DevController
 
 import xen.xend.XendProtocol
 
@@ -107,36 +108,59 @@ def diagnose_devices():
                 print ("Cannot find backend path for device %s, %s." %
                        (deviceClass, device))
             else:
-                backend_error = xstransact.Read(
-                    backendPath.replace('backend/', 'error/backend/'),
-                    'error')
+                frontend_state = xstransact.Read(frontendPath, 'state')
+                backend_state  = xstransact.Read(backendPath,  'state')
 
-                if backend_error:
-                    diagnose_device_error(backend_error)
+                print "Backend is in state %s." %  stateString(backend_state)
+                print "Frontend is in state %s." % stateString(frontend_state)
 
+                check_for_error(True)
+                check_for_error(False)
 
-def diagnose_device_error(err):
-    if re.search("2 reading .*/ring-ref and event-channel", err):
-        print ("Backend is stuck waiting for frontend for device %s, %s." %
-               (deviceClass, device))
-        diagnose_stuck_frontend()
+                diagnose_hotplugging()
+
+
+def check_for_error(backend):
+    if backend:
+        path = backendPath.replace('backend/', 'error/backend/')
     else:
-        print ("Device %s, %s shows error %s." %
-               (deviceClass, device, err))
+        path = frontendPath.replace('device/', 'error/device/')
+
+    err = xstransact.Read(path, 'error')
+
+    if err:
+        print ("%s for device %s, %s shows error %s." %
+               (backend and 'Backend' or 'Frontend', deviceClass, device,
+                err))
 
 
-def diagnose_stuck_frontend():
-    if deviceClass == "vbd":
+def diagnose_hotplugging():
+    if deviceClass == 'vbd':
         phy = xstransact.Read(backendPath, 'physical-device')
 
         if phy:
-            print ("Device %s, %s hotplugging has completed successfully." %
+            print ('Device %s, %s hotplugging has completed successfully, '
+                   'and is connected to physical device %s.' %
+                   (deviceClass, device, phy))
+        else:
+            print ('Device %s, %s hotplugging failed.' %
                    (deviceClass, device))
+    elif deviceClass == 'vif':
+        handle = xstransact.Read(backendPath, 'handle')
+
+        if handle:
+            print ('Device %s, %s hotplugging has completed successfully, '
+                   'and is using handle %s.' %
+                   (deviceClass, device, handle))
         else:
-            print ("Device %s, %s hotplugging failed." %
+            print ('Device %s, %s hotplugging failed.' %
                    (deviceClass, device))
 
 
+def stateString(state):
+    return state and DevController.xenbusState[int(state)] or '<None>'
+
+
 def main(argv = None):
     if argv is None:
         argv = sys.argv
index f648562c1afb3bb0565d497e4692e20ae14a0cdb..3e4853fbf047681770959adf1b5470f75b7833d0 100644 (file)
@@ -25,10 +25,23 @@ from xen.xend.XendLogging import log
 from xen.xend.xenstore.xstransact import xstransact
 from xen.xend.xenstore.xswatch import xswatch
 
-DEVICE_CREATE_TIMEOUT = 120
+DEVICE_CREATE_TIMEOUT = 5
 HOTPLUG_STATUS_NODE = "hotplug-status"
 HOTPLUG_STATUS_ERROR = "error"
 
+xenbusState = {
+    'Unknown'      : 0,
+    'Initialising' : 1,
+    'InitWait'     : 2,
+    'Initialised'  : 3,
+    'Connected'    : 4,
+    'Closing'      : 5,
+    'Closed'       : 6,
+    }
+
+xenbusState.update(dict(zip(xenbusState.values(), xenbusState.keys())))
+
+
 class DevController:
     """Abstract base class for a device controller.  Device controllers create
     appropriate entries in the store to trigger the creation, reconfiguration,
@@ -116,10 +129,8 @@ class DevController:
         frontpath = self.frontendPath(devid)
         backpath = xstransact.Read(frontpath, "backend")
 
-        xstransact.Remove(frontpath)
-
         if backpath:
-            xstransact.Remove(backpath)
+            xstransact.Write(backpath, 'state', str(xenbusState['Closing']))
         else:
             raise VmError("Device %s not connected" % devid)
            
@@ -257,18 +268,18 @@ class DevController:
         frontpath = self.frontendPath(devid)
         backpath  = self.backendPath(backdom, devid)
         
-        xstransact.Remove(backpath, HOTPLUG_STATUS_NODE)
-
         frontDetails.update({
             'backend' : backpath,
-            'backend-id' : "%i" % backdom.getDomid()
+            'backend-id' : "%i" % backdom.getDomid(),
+            'state' : str(xenbusState['Initialising'])
             })
 
 
         backDetails.update({
             'domain' : self.vm.getName(),
             'frontend' : frontpath,
-            'frontend-id' : "%i" % self.vm.getDomid()
+            'frontend-id' : "%i" % self.vm.getDomid(),
+            'state' : str(xenbusState['Initialising'])
             })
 
         log.debug('DevController: writing %s to %s.', str(frontDetails),
@@ -276,8 +287,20 @@ class DevController:
         log.debug('DevController: writing %s to %s.', str(backDetails),
                   backpath)
 
-        xstransact.Write(frontpath, frontDetails)
-        xstransact.Write(backpath, backDetails)
+        while True:
+            t = xstransact()
+            try:
+                t.remove2(backpath, HOTPLUG_STATUS_NODE)
+
+                t.write2(frontpath, frontDetails)
+                t.write2(backpath,  backDetails)
+
+                if t.commit():
+                    return
+            except:
+                t.abort()
+                raise
+
 
     def waitForBackend(self,devid):
         ev = Event()
index c1b72a970fffaddf05499936d40d8c82bfda881b..ac06c2b1b8ab47119cbb41995bc5787599ca097e 100644 (file)
@@ -10,7 +10,7 @@ from xen.xend.xenstore.xsutil import xshandle
 
 class xstransact:
 
-    def __init__(self, path):
+    def __init__(self, path = ""):
         assert path is not None
         
         self.in_transaction = False # Set this temporarily -- if this
@@ -41,7 +41,7 @@ class xstransact:
         return rc
 
     def _read(self, key):
-        path = "%s/%s" % (self.path, key)
+        path = self.prependPath(key)
         try:
             return xshandle().read(self.transaction, path)
         except RuntimeError, ex:
@@ -66,7 +66,7 @@ class xstransact:
         return ret
 
     def _write(self, key, data):
-        path = "%s/%s" % (self.path, key)
+        path = self.prependPath(key)
         try:
             xshandle().write(self.transaction, path, data)
         except RuntimeError, ex:
@@ -99,7 +99,7 @@ class xstransact:
             raise TypeError
 
     def _remove(self, key):
-        path = "%s/%s" % (self.path, key)
+        path = self.prependPath(key)
         return xshandle().rm(self.transaction, path)
 
     def remove(self, *args):
@@ -114,7 +114,7 @@ class xstransact:
                 self._remove(key)
 
     def _list(self, key):
-        path = "%s/%s" % (self.path, key)
+        path = self.prependPath(key)
         l = xshandle().ls(self.transaction, path)
         if l:
             return map(lambda x: key + "/" + x, l)
@@ -215,6 +215,30 @@ class xstransact:
                 self._write(key, fmt % val)
 
 
+    def remove2(self, middlePath, *args):
+        self.callRebased(middlePath, self.remove, *args)
+
+
+    def write2(self, middlePath, *args):
+        self.callRebased(middlePath, self.write, *args)
+
+
+    def callRebased(self, middlePath, func, *args):
+        oldpath = self.path
+        self.path = self.prependPath(middlePath)
+        try:
+            func(*args)
+        finally:
+            self.path = oldpath
+
+
+    def prependPath(self, key):
+        if self.path:
+            return self.path + '/' + key
+        else:
+            return key
+
+
     def Read(cls, path, *args):
         """If only one argument is given (path), return the value stored at
         that path.  If two arguments are given, treat the second argument as a